Tại sao tôi không thể tạo vectơ lambdas (cùng loại) trong C ++ 11?


88

Tôi đã cố gắng tạo một vectơ lambda, nhưng không thành công:

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

Cho đến dòng số 2, nó biên dịch tốt . Nhưng dòng số 3 đưa ra lỗi biên dịch :

lỗi: không có hàm phù hợp để gọi đến 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)'

Tôi không muốn một vectơ của con trỏ hàm hoặc vectơ của các đối tượng hàm. Tuy nhiên, vectơ của các đối tượng hàm đóng gói các biểu thức lambda thực , sẽ phù hợp với tôi. Điều này có khả thi không?


23
"Tôi không muốn một vectơ của con trỏ hàm hoặc vectơ của các đối tượng hàm." Nhưng đó là những gì bạn yêu cầu. Lambda một đối tượng hàm.
Nicol Bolas

Câu trả lời:


135

Mỗi lambda đều có một kiểu khác nhau — ngay cả khi chúng có cùng chữ ký. Bạn phải sử dụng một vùng chứa đóng gói thời gian chạy, chẳng hạn như std::functionnếu bạn muốn làm điều gì đó tương tự.

ví dụ:

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

52
Quản lý một đội ngũ nhà phát triển hàng trăm người đàn ông có vẻ giống như một cơn ác mộng đối với tôi :)
Jeremy Friesner

10
Ngoài ra, đừng quên rằng lambdas không chụp (kiểu []) có thể biến chất thành con trỏ hàm. Vì vậy, anh ta có thể lưu trữ một mảng các con trỏ hàm cùng kiểu. Lưu ý rằng VC10 chưa thực hiện điều đó.
Nicol Bolas

Nhân tiện, không nên sử dụng capture-less trong những ví dụ đó sao? Hay là nó cần thiết? - Nhân tiện, lambda không chụp tới con trỏ hàm dường như được hỗ trợ trong VC11. Đã không kiểm tra nó mặc dù.
Klaim

2
Có thể tạo một hàm lưu trữ vectơ kiểu khác không? tức là thay vì giới hạn nó std::function<int(), tôi có thể sử dụng các nguyên mẫu hàm khác nhau không?
manatttta

2
@manatttta Vấn đề sẽ là gì? Container tồn tại để lưu trữ các đối tượng cùng loại, để sắp xếp và thao tác chúng với nhau. Bạn cũng có thể hỏi 'tôi có thể tạo một vectorkho lưu trữ cả std::functionstd::stringkhông?' Và câu trả lời là như nhau: Không, vì đó không phải là mục đích sử dụng. Bạn có thể sử dụng một lớp kiểu 'biến thể' để thực hiện đủ kiểu xóa để đặt những thứ khác nhau vào một vùng chứa, trong khi bao gồm một phương thức để người dùng xác định kiểu 'thực' và do đó chọn phải làm gì với (ví dụ: cách gọi) mỗi phần tử ... nhưng một lần nữa, tại sao lại có độ dài như vậy? Có bất kỳ cơ sở lý do thực sự?
underscore_d

40

Tất cả các biểu thức lambda có một kiểu khác nhau, ngay cả khi chúng giống hệt nhau theo từng ký tự . Bạn đang đẩy một lambda thuộc một kiểu khác (vì đó là một biểu thức khác) vào trong vectơ, và điều đó rõ ràng sẽ không hoạt động.

Một giải pháp là tạo một vector std::function<int()>thay thế.

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

Một lưu ý khác, không nên sử dụng [&]khi bạn không chụp bất cứ thứ gì.


14
Không cần ()lambdas không có đối số.
Puppy

18

Trong khi những gì những người khác đã nói là có liên quan, vẫn có thể khai báo và sử dụng một vectơ lambda, mặc dù nó không hữu ích lắm:

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

Vì vậy, bạn có thể lưu trữ bất kỳ số lượng lambdas nào trong đó, miễn là đó là bản sao / chuyển của lambda!


Điều này thực sự có thể hữu ích nếu phản hồi xảy ra trong một vòng lặp với các tham số khác nhau. Có lẽ cho mục đích đánh giá lười biếng.
MaHuJa

7
Không bạn không đặt các thông số trong vector, chỉ cần đối tượng chức năng .. Vì vậy, nó sẽ là một vector với tất cả các bản sao của lambda cùng
hariseldon78

16

Nếu lambda của bạn là không trạng thái, tức là [](...){...}, C ++ 11 cho phép nó biến thành một con trỏ hàm. Về lý thuyết, một trình biên dịch tuân thủ C ++ 11 sẽ có thể biên dịch điều này:

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

4
Đối với hồ sơ auto ignore = *[] { return 10; };sẽ làm cho ignoremột int(*)().
Luc Danton

1
@Luc, ôi ghê quá! Khi nào họ thêm điều đó?
MSN

3
Chà, vì hàm chuyển đổi cho phép lấy một con trỏ hàm ở vị trí đầu tiên không được phép explicit, nên việc bỏ tham chiếu biểu thức lambda là hợp lệ và bỏ tham chiếu đến con trỏ tạo ra từ chuyển đổi. Sau đó, sử dụng autophân rã tham chiếu đó trở lại thành một con trỏ. (Sử dụng auto&hoặc auto&&có thể đã giữ tham chiếu.)
Luc Danton

Ah ... Tham chiếu đến con trỏ kết quả. Điều đó có lý. Việc mất tích là ()cố ý hay vô tình?
MSN

Theo chủ ý, biểu thức lambda là tương đương (nhưng ngắn hơn hai ký tự).
Luc Danton

6

Bạn có thể sử dụng hàm tạo lambda (được cập nhật với bản sửa lỗi do Nawaz đề xuất):

#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++;
    }
}

Nhưng tôi nghĩ về cơ bản bạn đã tạo ra lớp học của riêng mình vào thời điểm này. Nếu không, nếu lambdas có caputres / args hoàn toàn khác nhau, v.v. thì bạn có thể phải sử dụng một bộ tuple.


Ý tưởng hay khi gói nó trong một chức năng giống như một hàm lambda_genlambda. Tuy nhiên, auto a = lambda_gen(1);thực hiện một cuộc gọi không cần thiết, điều này có thể tránh được nếu chúng ta viết điều này decltype(lambda_gen(1)).
Nawaz

Tuy nhiên, điều đó vẫn không tạo ra một cuộc gọi bổ sung? Ngoài ra, một điểm nhỏ khác là câu hỏi nêu rõ C ++ 11 nên tôi nghĩ sẽ cần thêm kiểu trả về theo sau vào hàm.
antediluvian,

Không. Bất cứ thứ gì bên trong decltype đều không được đánh giá , vì vậy cuộc gọi không thực sự được thực hiện. Đó là trường hợp tương tự với sizeof. Ngoài ra, mã này sẽ không hoạt động trong C ++ 11 ngay cả khi bạn thêm kiểu trả về theo sau !!
Nawaz

4

Mỗi lambda là một loại khác nhau. Bạn phải sử dụng std::tuplethay vì std::vector.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.