Lambda chụp như tham chiếu const?


166

Có thể chụp bằng tham chiếu const trong biểu thức lambda không?

Tôi muốn bài tập được đánh dấu bên dưới không thành công, ví dụ:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

Cập nhật: Vì đây là một câu hỏi cũ, có thể tốt để cập nhật nó nếu có các phương tiện trong C ++ 14 để trợ giúp điều này. Các phần mở rộng trong C ++ 14 có cho phép chúng tôi chụp một đối tượng không phải là const bằng tham chiếu const không? ( Tháng 8 năm 2015 )


lambda của bạn không giống như : [&, &best_string](string const s) { ...}?
erjot

3
thực sự chụp không nhất quán. "const &" có thể rất hữu ích khi bạn có đối tượng const lớn cần truy cập nhưng không được sửa đổi trong hàm lambda
sergtk

nhìn vào mã bạn có thể sử dụng hai tham số lambda và liên kết thứ hai như một tham chiếu const. đi kèm với một chi phí mặc dù.
Alex

1
Điều này dường như không thể có trong C ++ 11. Nhưng có lẽ chúng ta có thể cập nhật câu hỏi này cho C ++ 14 - có phần mở rộng nào cho phép điều này không? Các lambda C ++ 14 tổng quát chụp?
Aaron McDaid

Câu trả lời:


127

const không có trong ngữ pháp để chụp vào lúc n3092:

capture:
  identifier
  & identifier
  this

Văn bản chỉ đề cập đến bản sao chụp và bản sao lưu của tài liệu tham khảo và không đề cập đến bất kỳ loại hình nào.

Cảm thấy như một sự giám sát đối với tôi, nhưng tôi đã không theo dõi quá trình tiêu chuẩn hóa rất chặt chẽ.


47
Tôi chỉ theo dõi một lỗi trở lại một biến được sửa đổi từ bản chụp có thể thay đổi, nhưng đáng lẽ phải có const. Hay chính xác hơn, nếu biến bắt giữ là const, trình biên dịch sẽ thực thi hành vi đúng trên lập trình viên. Sẽ tốt hơn nếu cú ​​pháp được hỗ trợ [&mutableVar, const &constVar].
Sean

Có vẻ như điều này có thể xảy ra với C ++ 14, nhưng tôi không thể làm cho nó hoạt động được. Bất kỳ đề xuất?
Aaron McDaid

37
Constness được kế thừa từ biến bị bắt. Vì vậy, nếu bạn muốn chụp anhư const, hãy khai báo const auto &b = a;trước lambda và chụpb
StenSoft

7
@StenSoft Bleargh. Ngoại trừ rõ ràng điều này không áp dụng khi bắt một biến thành viên bằng cách tham chiếu: [&foo = this->foo]bên trong consthàm cho tôi một lỗi cho biết rằng bản thân chụp sẽ loại bỏ vòng loại. Đây có thể là một lỗi trong GCC 5.1, tuy nhiên, tôi cho rằng.
Kyle Strand

119

Trong sử dụng static_cast/ const_cast:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

BẢN GIỚI THIỆU


Trong sử dụng std::as_const:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO 2


Ngoài ra, có lẽ điều này nên được chỉnh sửa thành câu trả lời được chấp nhận? Dù bằng cách nào, nên có một câu trả lời hay bao gồm cả c ++ 11 và c ++ 14. Mặc dù, tôi đoán có thể lập luận rằng c ++ 14 sẽ đủ tốt cho tất cả mọi người trong những năm tới
Aaron McDaid

12
@AaronMcDaid const_castcó thể thay đổi vô điều kiện một đối tượng dễ bay hơi thành một đối tượng const (khi được yêu cầu chuyển sang const), do đó, để thêm các ràng buộc tôi thíchstatic_cast
Piotr Skotnicki

1
Mặt khác, @PiotrSkotnicki, static_castđể tham chiếu có thể âm thầm tạo tạm thời nếu bạn không có được loại chính xác
MM

24
@MM &basic_string = std::as_const(best_string)nên giải quyết tất cả các vấn đề
Piotr Skotnicki

14
@PiotrSkotnicki ngoại trừ vấn đề đó là một cách gớm ghiếc để ghi cái gì đó nên thể đơn giản như const& best_string.
Kyle Strand

12

Tôi nghĩ rằng phần chụp không nên chỉ định const, vì phần chụp có nghĩa, nó chỉ cần một cách để truy cập biến phạm vi bên ngoài.

Các specifier được chỉ định tốt hơn trong phạm vi bên ngoài.

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

Hàm lambda là const (không thể thay đổi giá trị trong phạm vi của nó), vì vậy khi bạn nắm bắt biến theo giá trị, biến không thể thay đổi, nhưng tham chiếu không nằm trong phạm vi lambda.


1
@Amarnath Balasubramani: Đó chỉ là ý kiến ​​của tôi, tôi nghĩ rằng không cần chỉ định một tham chiếu const trong phần chụp lambda, tại sao nên có một biến const ở đây và không phải là const ở một nơi khác (nếu có thể, nó sẽ dễ bị lỗi ). rất vui khi thấy phản hồi của bạn
zhb

2
Nếu bạn cần sửa đổi better_stringtrong phạm vi chứa, thì giải pháp này sẽ không hoạt động. Trường hợp sử dụng để chụp dưới dạng const-ref là khi biến cần biến đổi trong phạm vi chứa nhưng không nằm trong lambda.
Jonathan Sharman

@JonathanSharman, bạn không mất gì khi tạo tham chiếu const cho một biến, vì vậy bạn có thể thực hiện const string &c_better_string = better_string;và vui vẻ chuyển nó cho lambda:[&c_better_string]
Steed

@Steed Vấn đề với điều đó là bạn đang giới thiệu một tên biến phụ vào phạm vi xung quanh. Tôi nghĩ rằng giải pháp của Piotr Skotnick ở trên là sạch nhất, vì nó đạt được tính chính xác trong khi vẫn giữ phạm vi thay đổi tối thiểu.
Jonathan Sharman

@JonathanSharman, ở đây chúng ta bước vào vùng đất của những ý kiến ​​- những gì đẹp nhất, hoặc sạch nhất, hoặc bất cứ điều gì. Quan điểm của tôi là cả hai giải pháp đều phù hợp với nhiệm vụ.
Steed

8

Tôi đoán nếu bạn không sử dụng biến làm tham số của functor, thì bạn nên sử dụng mức truy cập của hàm hiện tại. Nếu bạn nghĩ rằng bạn không nên, thì hãy tách lambda của bạn khỏi chức năng này, nó không phải là một phần của nó.

Dù sao, bạn có thể dễ dàng đạt được điều tương tự mà bạn muốn bằng cách sử dụng tham chiếu const khác thay thế:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

Nhưng điều đó cũng giống như giả sử rằng lambda của bạn phải được cách ly khỏi chức năng hiện tại, làm cho nó không phải là lambda.


1
Điều khoản chụp vẫn best_stringchỉ đề cập . Ngoài ra, GCC 4.5 "từ chối thành công" mã như dự định.
sellibitze

Vâng, điều này sẽ cho tôi kết quả mà tôi đã cố gắng đạt được ở cấp độ kỹ thuật. Cuối cùng, câu trả lời cho câu hỏi ban đầu của tôi dường như là "không."
John Dibling

Tại sao điều đó làm cho nó trở thành một "non-lambda"?

Bởi vì bản chất của lambda là nó phụ thuộc vào ngữ cảnh. Nếu bạn không cần một bối cảnh cụ thể thì đó chỉ là một cách nhanh chóng để tạo ra một functor. Nếu functor nên độc lập theo ngữ cảnh, hãy biến nó thành một functor thực sự.
Klaim

3
"Nếu functor nên độc lập với bối cảnh, hãy biến nó thành một functor thực sự" ... và có thể hôn tạm biệt?
Andrew Lazarus

5

Tôi nghĩ bạn có ba lựa chọn khác nhau:

  • không sử dụng tham chiếu const, nhưng sử dụng một bản sao chụp
  • bỏ qua thực tế rằng nó có thể sửa đổi
  • sử dụng std :: bind để liên kết một đối số của hàm nhị phân có tham chiếu const.

sử dụng một bản sao

Phần thú vị về lambdas với các bản sao chụp là những cái đó thực sự chỉ được đọc và do đó làm chính xác những gì bạn muốn chúng.

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

sử dụng std :: bind

std::bindlàm giảm tính tự nhiên của một chức năng. Tuy nhiên, lưu ý rằng điều này có thể / sẽ dẫn đến một cuộc gọi chức năng gián tiếp thông qua một con trỏ hàm.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}

1
Ngoại trừ những thay đổi đối với biến trong phạm vi chứa sẽ không được phản ánh trong lambda. Nó không phải là một tài liệu tham khảo, nó chỉ là một biến không nên được gán lại bởi vì việc gán lại sẽ không có nghĩa là nó có nghĩa gì.
Grault


0

Sử dụng tiếng kêu hoặc đợi cho đến khi lỗi gcc này được sửa: bug 70385: Việc bắt Lambda bằng cách tham chiếu tham chiếu const không thành công [ https://gcc.gnu.org/ormszilla/show_orms.cgi?id=70385 ]


1
Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
Div

Ok, tôi đã chỉnh sửa câu trả lời của mình để thêm mô tả lỗi gcc ở đây.
dùng1436126

Đây là một câu trả lời gián tiếp cho câu hỏi, nếu có. Lỗi là về cách trình biên dịch thất bại khi chụp một cái gì đó const, vì vậy có lẽ tại sao một số cách để giải quyết hoặc giải quyết vấn đề trong câu hỏi có thể không hoạt động với gcc.
Stein

0

Sử dụng một const sẽ chỉ đơn giản là có các bộ giải thuật toán và đặt chuỗi thành giá trị ban đầu của nó, nói cách khác, lambda sẽ không thực sự tự xác định là tham số của hàm, mặc dù phạm vi xung quanh sẽ có thêm một biến ... Không cần xác định nó mặc dù vậy, nó sẽ không định nghĩa chuỗi là [&, & best_ chuỗi] (chuỗi const s) điển hình . Do đó , rất có thể sẽ tốt hơn nếu chúng ta chỉ để nó ở đó, cố gắng nắm bắt tham chiếu.


Đây là một câu hỏi rất cũ: câu trả lời của bạn thiếu ngữ cảnh liên quan đến phiên bản C ++ mà bạn đang đề cập đến. Vui lòng cung cấp nội dung này.
ZF007
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.