Hàm trả về một biểu thức lambda


88

Tôi tự hỏi liệu có thể viết một hàm trả về một hàm lambda trong C ++ 11 không. Tất nhiên một vấn đề là làm thế nào để khai báo hàm như vậy. Mỗi lambda có một kiểu, nhưng kiểu đó không thể diễn đạt được trong C ++. Tôi không nghĩ điều này sẽ hiệu quả:

auto retFun() -> decltype ([](int x) -> int)
{
    return [](int x) { return x; }
}

Cũng không phải cái này:

int(int) retFun();

Tôi không biết về bất kỳ chuyển đổi tự động nào từ lambdas thành, chẳng hạn, con trỏ đến các hàm hoặc một số chuyển đổi tương tự. Giải pháp duy nhất có phải là tạo thủ công một đối tượng hàm và trả lại nó không?


1
Để thêm những gì đã được nói, các hàm lambda không trạng thái có thể chuyển đổi thành con trỏ hàm.
snk_kid

3
IMO tùy chọn đầu tiên của bạn sẽ không hoạt động kể từ khi lambda trong decltypekhông giống như trong cơ quan chức năng và do đó có một loại khác nhau (thậm chí nếu bạn đã bao gồm câu lệnh return)
Motti

1
Nhân tiện, nếu lambda có một mệnh đề chụp rỗng, nó có thể được chuyển đổi hoàn toàn thành một con trỏ để hoạt động.
GManNickG

@GMan: Trừ khi bạn đang sử dụng Visual C ++ 2010 hoặc phiên bản g ++ được phát hành hơn một năm trước (hoặc ở đó). Việc chuyển đổi ngầm định captureless-lambda thành con trỏ hàm đã không được thêm vào cho đến tháng 3 năm 2010 trong N3092.
James McNellis

4
Các biểu thức Lambda nói chung không thể xuất hiện trong các toán hạng không được đánh giá. Vì vậy, decltype([](){})hoặc sizeof([]() {})là xấu cho dù bạn viết nó ở đâu.
Johannes Schaub - litb

Câu trả lời:


98

Bạn không cần một đối tượng hàm được tạo thủ công, chỉ cần sử dụng std::function, các hàm lambda có thể chuyển đổi được:

Ví dụ này trả về hàm nhận dạng số nguyên:

std::function<int (int)> retFun() {
    return [](int x) { return x; };
}

6
Tât nhiên! Tôi yêu StackOverflow. Tôi sẽ mất nhiều thời gian hơn để nghiên cứu chủ đề sau đó nhận được câu trả lời trên StackOverflow. Cảm ơn Sean!
Bartosz Milewski

6
Điều đó sẽ gây ra cấp phát bộ nhớ mặc dù trong hàm tạo của std::function.
Maxim Egorushkin

2
@Maxim Yegorushkin std :: function có ngữ nghĩa di chuyển cộng với nó có thể sử dụng trình phân bổ tùy chỉnh và bản nháp làm việc C ++ 0x có những lưu ý sau: "[Lưu ý: việc triển khai được khuyến khích để tránh sử dụng bộ nhớ được cấp phát động cho các đối tượng nhỏ có thể gọi , trong đó đích của f là một đối tượng chỉ chứa một con trỏ hoặc tham chiếu đến một đối tượng và một con trỏ hàm thành viên. —Gửi ghi chú] "nên về cơ bản bạn không thể đưa ra nhiều giả định về chiến lược phân bổ mà một triển khai cụ thể đang sử dụng nhưng bạn có thể để sử dụng các trình phân bổ (gộp chung) của riêng bạn.
snk_kid

1
@Maxim: Câu trả lời của tôi là cho câu hỏi "Có phải giải pháp duy nhất là tạo thủ công một đối tượng hàm và trả về nó không?"
Sean

2
Chỉ cần lưu ý rằng std::functionsử dụng tính năng xóa kiểu, có thể có nghĩa là chi phí thực hiện một cuộc gọi hàm ảo khi gọi std::function. Một số điều cần lưu ý nếu hàm được trả về sẽ được sử dụng trong một vòng lặp chặt chẽ bên trong hoặc bối cảnh khác, nơi có vấn đề về tính kém hiệu quả nhỏ.
Anthony Hall

29

Đối với ví dụ đơn giản này, bạn không cần std::function.

Từ tiêu chuẩn §5.1.2 / 6:

Kiểu đóng đối với biểu thức lambda không có lambda-capture có hàm chuyển đổi const không rõ ràng không ảo công khai thành con trỏ tới hàm có cùng tham số và kiểu trả về như toán tử gọi hàm của kiểu đóng. Giá trị được trả về bởi hàm chuyển đổi này sẽ là địa chỉ của một hàm mà khi được gọi ra, nó có tác dụng giống như việc gọi toán tử gọi hàm của kiểu bao đóng.

Bởi vì hàm của bạn không có chức năng chụp, điều đó có nghĩa là lambda có thể được chuyển đổi thành một con trỏ thành hàm kiểu int (*)(int):

typedef int (*identity_t)(int); // works with gcc
identity_t retFun() { 
  return [](int x) { return x; };
}

Đó là sự hiểu biết của tôi, hãy sửa cho tôi nếu tôi sai.


1
Điều này nghe có vẻ đúng. Thật không may, nó không hoạt động với trình biên dịch hiện tại tôi đang sử dụng: VS 2010. std :: chức năng chuyển đổi vẫn hoạt động.
Bartosz Milewski

3
Có, từ ngữ cuối cùng của quy tắc này đến quá muộn cho VC2010.
Ben Voigt

Tôi đã thêm ví dụ về mã. Đây là một chương trình đầy đủ .
jfs

@JFSebastian - Tuổi thọ của lambda trong ví dụ này là bao nhiêu? Có đủ lâu để tồn tại lâu hơn kết quả của việc chuyển đổi thành con trỏ hàm không?
Flexo

1
@JFSebastian - Tôi dường như không thể tìm ra câu trả lời cho điều đó nên tôi đã hỏi nó như một câu hỏi theo đúng nghĩa của nó: stackoverflow.com/questions/8026170/…
Flexo

21

Bạn có thể trả về hàm lambda từ hàm lambda khác, vì bạn không nên chỉ định rõ ràng kiểu trả về của hàm lambda. Chỉ cần viết một cái gì đó như vậy trong phạm vi toàn cầu:

 auto retFun = []() {
     return [](int x) {return x;};
 };

2
Điều đó chỉ đúng khi lambda bên ngoài chỉ bao gồm câu lệnh return. Nếu không, bạn phải chỉ định kiểu trả về.
Bartosz Milewski

3
Đây là câu trả lời tốt nhất vì nó không đòi hỏi đa hình thời gian chạy của std :: chức năng và cho phép lambda để có một danh sách chụp không trống, tuy nhiên tôi sẽ sử dụng const auto fun = ...
robson3.14

2
@BartoszMilewski không đúng với C ++ 14.
dzhioev

19

Mặc dù câu hỏi đặc biệt hỏi về C ++ 11, vì lợi ích của những người khác tình cờ gặp phải điều này và có quyền truy cập vào trình biên dịch C ++ 14, C ++ 14 hiện cho phép các kiểu trả về được suy diễn cho các hàm thông thường. Vì vậy, ví dụ trong câu hỏi có thể được điều chỉnh để hoạt động như mong muốn chỉ đơn giản bằng cách bỏ -> decltypemệnh đề ... sau danh sách tham số hàm:

auto retFun()
{
    return [](int x) { return x; }
}

Tuy nhiên, lưu ý rằng điều này sẽ không hoạt động nếu nhiều hơn một return <lambda>;xuất hiện trong hàm. Điều này là do một hạn chế trong việc khấu trừ kiểu trả về là tất cả các câu lệnh trả về phải trả về các biểu thức cùng kiểu, nhưng mọi đối tượng lambda đều được trình biên dịch cung cấp kiểu duy nhất của riêng nó, vì vậy mỗi return <lambda>;biểu thức sẽ có một kiểu khác nhau.


4
Tại sao lại đề cập đến các kiểu suy luận của c ++ 14 nhưng lại bỏ qua lambdas đa hình? auto retFun() { return [](auto const& x) { return x; }; }
xem

1

Bạn nên viết như thế này:

auto returnFunction = [](int x){
    return [&x](){
        return x;
    }();
};

để lấy trả về của bạn dưới dạng một hàm và sử dụng nó như:

int val = returnFunction(someNumber);

0

Ví dụ: nếu bạn không có c ++ 11 và đang chạy mã c ++ trên bộ điều khiển vi mô. Bạn có thể trả về một con trỏ void và sau đó thực hiện ép kiểu.

void* functionThatReturnsLambda()
{
    void(*someMethod)();

    // your lambda
    someMethod = []() {

        // code of lambda

    };

    return someMethod;
}


int main(int argc, char* argv[])
{

    void* myLambdaRaw = functionThatReturnsLambda();

    // cast it
    auto myLambda = (void(*)())myLambdaRaw;

    // execute lambda
    myLambda();
}
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.