เหตุใดฉันจึงไม่สามารถสร้างเวกเตอร์ของ lambdas (ชนิดเดียวกัน) ใน C ++ 11 ได้


90

ฉันพยายามสร้างเวกเตอร์แลมด้า แต่ล้มเหลว:

auto ignore = [&]() { return 10; };  //1
std::vector<decltype(ignore)> v;     //2
v.push_back([&]() { return 100; });  //3

ขึ้นกับสาย # 2 ก็รวบรวมดี แต่บรรทัด # 3 ให้ข้อผิดพลาดในการคอมไพล์ :

ข้อผิดพลาด: ไม่มีฟังก์ชันที่ตรงกันสำหรับการเรียกไปที่ 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)'

ฉันไม่ต้องการเวกเตอร์ของตัวชี้ฟังก์ชันหรือเวกเตอร์ของวัตถุฟังก์ชัน อย่างไรก็ตามเวกเตอร์ของวัตถุฟังก์ชันที่ห่อหุ้มนิพจน์แลมบ์ดาจริงจะใช้ได้สำหรับฉัน เป็นไปได้หรือไม่


23
"ฉันไม่ต้องการเวกเตอร์ของตัวชี้ฟังก์ชันหรือเวกเตอร์ของวัตถุฟังก์ชัน" แต่นั่นคือสิ่งที่คุณขอ แลมบ์ดาเป็นวัตถุฟังก์ชัน
Nicol Bolas

คำตอบ:


137

แลมบ์ดาทุกตัวมีประเภทที่แตกต่างกันแม้ว่าจะมีลายเซ็นเดียวกันก็ตาม คุณต้องใช้คอนเทนเนอร์ที่ห่อหุ้มรันไทม์เช่นstd::functionถ้าคุณต้องการทำอะไรแบบนั้น

เช่น:

std::vector<std::function<int()>> functors;
functors.push_back([&] { return 100; });
functors.push_back([&] { return  10; });

52
การจัดการทีมนักพัฒนาร้อยคนฟังดูเหมือนฝันร้ายสำหรับฉัน :)
Jeremy Friesner

10
นอกจากนี้อย่าลืมว่า lambdas ([] -style) ที่ไม่มีการจับภาพสามารถย่อยสลายเป็นตัวชี้ฟังก์ชันได้ ดังนั้นเขาจึงสามารถจัดเก็บอาร์เรย์ของพอยน์เตอร์ฟังก์ชันประเภทเดียวกันได้ โปรดทราบว่า VC10 ยังไม่ได้ใช้งาน
Nicol Bolas

ยังไงก็ตามไม่ควรใช้การดักจับน้อยในตัวอย่างเหล่านั้นใช่ไหม หรือว่าจำเป็น? - อย่างไรก็ตามแลมบ์ดาที่ไม่มีการจับภาพเพื่อใช้งานตัวชี้ดูเหมือนจะได้รับการสนับสนุนใน VC11 ไม่ได้ทดสอบเลย
Klaim

2
เป็นไปได้หรือไม่ที่จะสร้างฟังก์ชันการจัดเก็บเวกเตอร์ประเภทต่างๆ เช่นแทนที่จะ จำกัด ให้std::function<int()ใช้ต้นแบบฟังก์ชันอื่นได้หรือไม่
manatttta

2
@manatttta จะเป็นยังไง? คอนเทนเนอร์มีไว้เพื่อจัดเก็บอ็อบเจ็กต์ประเภทเดียวกันเพื่อจัดระเบียบและจัดการเข้าด้วยกัน คุณอาจถามเช่นกันว่า 'ฉันสามารถสร้างที่vectorจัดเก็บทั้งสองstd::functionและstd::string?' และคำตอบก็เหมือนกัน: ไม่เพราะนั่นไม่ใช่การใช้งานที่ตั้งใจไว้ คุณสามารถใช้คลาสสไตล์ 'ตัวแปร' เพื่อทำการลบประเภทให้เพียงพอเพื่อใส่สิ่งที่แตกต่างกันในคอนเทนเนอร์ในขณะที่รวมวิธีการสำหรับผู้ใช้ในการกำหนดประเภท 'ของจริง' และด้วยเหตุนี้จึงเลือกว่าจะทำอย่างไร (เช่นวิธีการโทร) แต่ละองค์ประกอบ ... แต่อีกครั้งทำไมต้องยาวขนาดนั้น? มีเหตุผลจริงหรือไม่?
underscore_d

41

นิพจน์แลมบ์ดาทั้งหมดมีประเภทที่แตกต่างกันแม้ว่าจะเหมือนกันทีละอักขระก็ตาม คุณกำลังดันแลมด้าประเภทอื่น (เนื่องจากเป็นนิพจน์อื่น) ลงในเวกเตอร์และเห็นได้ชัดว่าไม่ได้ผล

ทางออกหนึ่งคือการสร้างเวกเตอร์std::function<int()>แทน

auto ignore = [&]() { return 10; };
std::vector<std::function<int()>> v;
v.push_back(ignore);
v.push_back([&]() { return 100; });

ในบันทึกย่ออื่นคุณไม่ควรใช้[&]เมื่อคุณไม่ได้จับภาพอะไรเลย


14
ไม่ต้องการแลม()บ์ดาที่ไม่มีข้อโต้แย้ง
Puppy

18

แม้ว่าสิ่งที่คนอื่นพูดจะเกี่ยวข้อง แต่ก็ยังสามารถประกาศและใช้เวกเตอร์ของแลมบ์ดาได้แม้ว่าจะไม่มีประโยชน์มากนัก:

auto lambda = [] { return 10; };
std::vector<decltype(lambda)> vec;
vec.push_back(lambda);

ดังนั้นคุณสามารถเก็บแลมบ์ดาจำนวนเท่าใดก็ได้ตราบใดที่เป็นสำเนา / ย้ายlambda!


ซึ่งอาจเป็นประโยชน์หากการส่งคืนเกิดขึ้นในลูปที่มีพารามิเตอร์ต่างกัน น่าจะเป็นไปเพื่อวัตถุประสงค์ในการประเมินที่ขี้เกียจ
MaHuJa

7
ไม่คุณไม่ได้ใส่พารามิเตอร์ในเวกเตอร์เพียงแค่วัตถุฟังก์ชัน .. ดังนั้นมันจะเป็นเวกเตอร์ที่มีสำเนาแลม
ด้า

16

หากแลมบ์ดาของคุณไม่มีสถานะกล่าวคือ[](...){...}C ++ 11 จะยอมให้มันย่อยสลายเป็นตัวชี้ฟังก์ชัน ในทางทฤษฎีคอมไพเลอร์ที่สอดคล้องกับ C ++ 11 จะสามารถรวบรวมสิ่งนี้ได้:

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3

4
สำหรับบันทึกที่auto ignore = *[] { return 10; };จะทำให้ ignoreint(*)()
Luc Danton

1
@ ลุคโอ้มันแย่มาก! พวกเขาเพิ่มเมื่อใด
MSN

3
เนื่องจากฟังก์ชันการแปลงที่อนุญาตให้ใช้ตัวชี้ฟังก์ชันในตำแหน่งแรกนั้นได้รับคำสั่งให้ไม่เป็นexplicitเช่นนั้นการยกเลิกการอ้างอิงนิพจน์แลมบ์ดาจึงถูกต้องและหักล้างตัวชี้ที่เป็นผลมาจากการแปลง จากนั้นใช้การautoสลายการอ้างอิงนั้นกลับเป็นตัวชี้ (ใช้auto&หรือauto&&จะเก็บไว้อ้างอิง)
Luc Danton

อ่า ... กำลังอ้างอิงตัวชี้ผลลัพธ์ ที่สมเหตุสมผล การหายไป()โดยเจตนาหรือโดยบังเอิญ?
MSN

โดยเจตนาการแสดงออกของแลมบ์ดาเทียบเท่ากัน (แต่สั้นกว่าสองอักขระ)
Luc Danton

6

คุณสามารถใช้ฟังก์ชันการสร้างแลมบ์ดา (อัปเดตด้วยการแก้ไขที่แนะนำโดย Nawaz):

#include <vector>
#include <iostream>

int main() {
    auto lambda_gen = [] (int i) {return [i](int x){ return i*x;};} ;

    using my_lambda = decltype(lambda_gen(1));

    std::vector<my_lambda> vec;

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i));

    int i = 0;

    for (auto& lambda : vec){
        std::cout << lambda(i) << std::endl;
        i++;
    }
}

แต่ฉันคิดว่าโดยพื้นฐานแล้วคุณได้สร้างชั้นเรียนของคุณเอง ณ จุดนี้ มิฉะนั้นถ้า lambdas มี caputres / args ต่างกันโดยสิ้นเชิงคุณอาจต้องใช้ tuple


ความคิดที่ดีที่จะห่อไว้ในฟังก์ชั่นlambda_genที่สามารถเปลี่ยนเป็นแลมด้าได้เอง แต่ทำให้การโทรที่ไม่จำเป็นซึ่งสามารถหลีกเลี่ยงได้ถ้าเราเขียนนี้auto a = lambda_gen(1); decltype(lambda_gen(1))
Nawaz

ยังไม่โทรพิเศษอีกเหรอ จุดด้อยอีกประการหนึ่งคือคำถามระบุ C ++ 11 ดังนั้นจึงต้องเพิ่มประเภทผลตอบแทนต่อท้ายให้กับฟังก์ชันที่ฉันคิด
antediluvian

ภายในสิ่งใดฉบับdecltype คือunevaluatedดังนั้นการเรียกร้องไม่ได้ทำจริง ก็เป็นกรณีเดียวกันกับsizeofเช่นกัน นอกจากนี้รหัสนี้จะไม่ทำงานใน C ++ 11 แม้ว่าคุณจะเพิ่มประเภทผลตอบแทนต่อท้ายก็ตาม !!
Nawaz

4

แลมด้าแต่ละชนิดเป็นประเภทที่แตกต่างกัน คุณต้องใช้std::tupleแทนstd::vector.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.