2012-12-04 27 views
9

Tôi đang cố chụp một đối tượng const qua bản sao trong lambda (có thể thay đổi). Trình biên dịch của tôi tuy nhiên phàn nàn, rằng đối tượng đã capture là const.Đối tượng const đã sao chép trong kết cấu lambda không thể thay đổi được

Nếu không thể sao chép đối tượng dưới dạng không const thì không thể?

struct Foo 
{ 
    Foo(){} 
    void Func(){} 
}; 

int main() 
{ 
    const Foo foo; 
    [foo]() mutable { foo.Func(); }; 
} 

Biên soạn với g ++ 4.7.2:

testcase.cpp: In lambda function: 
testcase.cpp:10:29: error: no matching function for call to ‘Foo::Func() const’ 
testcase.cpp:10:29: note: candidate is: 
testcase.cpp:4:7: note: void Foo::Func() <near match> 
testcase.cpp:4:7: note: no known conversion for implicit ‘this’ parameter from ‘const Foo*’ to ‘Foo*’ 

Biên soạn với kêu vang ++ 3.1:

testcase.cpp:10:20: error: member function 'Func' not viable: 'this' argument has type 'const Foo', but function is not marked const 
    std::async([foo]() mutable { foo.Func(); }); 

Tài liệu chuẩn (hay đúng hơn là dự thảo ...) định nghĩa trong 5.1.2.14 rằng "Kiểu [...] là kiểu của thực thể được capture tương ứng", vì vậy tôi đoán rằng nó sẽ bao gồm các cv-specifiers.
Mặc dù nó không có vẻ trực quan.

+0

Bạn có thể sao chép rõ ràng bên trong cơ thể lambda của mình nhưng tôi đoán đó không phải là những gì bạn đang tìm kiếm. Tất nhiên bạn có thể lấy tham chiếu giá trị r làm tham số cho nó là C++ 11. – CashCow

+0

Câu hỏi là gì? – chill

+0

@chill Đây là câu hỏi: tại sao đối tượng foo được sao chép bên trong lambda là const? –

Trả lời

6

Thứ nhất, các loại của một biểu thức lambda, trong đó có chụp, là một loại lớp (5.1.2 biểu thức Lambda [expr.prim.lambda] # 3)

loại đó có một operator() mà là theo mặc định const, trừ khi mutable được sử dụng trong biểu thức lambda ([expr.prim.lambda] # 5)

Tiếp theo, đối với mỗi thực thể được chụp dưới dạng bản sao, một thành viên chưa được đặt tên được khai báo trong loại đóng. [expr.prim.lambda] # 14]

Nếu bạn xây dựng một cách rõ ràng (chủ yếu) tương đương với loại chụp, mọi thứ sẽ tự nhiên theo từ ngữ nghĩa thông thường cho các lớp, các loại đủ điều kiện const và hàm thành viên đủ điều kiện.

Ví dụ:

struct S 
{ 
    void f(); 
    void fc() const; 
}; 

void g() 
{ 
    S s0; 

    // [s0]() { s0.f(); }; // error, operator() is const 
    [s0]() { s0.fc(); }; // OK, operator() is const, S::fc is const 

    [s0]() mutable { s0.f(); }; 
    [s0]() mutable { s0.fc(); }; 

    const S s1; 

    // [s1]() { s1.f(); }; // error, s1 is const, no matter if operator() is const 
    [s1]() { s1.fc(); }; 

    // [s1]() mutable { s1.f(); }; // error, s1 is const, no matter if operator() is const 
    [s1]() mutable { s1.fc(); }; 
} 

Tôi đoán sự nhầm lẫn bắt nguồn từ thực tế là mutable trong lambda-declarator liên quan đến const -ness của operator(), không phải là mutable -ility của các thành viên dữ liệu của các loại đóng cửa . Sẽ là tự nhiên khi sử dụng const, như với các hàm thành viên, nhưng tôi đoán ủy ban tiêu chuẩn muốn const làm mặc định.

+0

Vâng, tôi đã tìm ra từ khóa 'mutable' làm gì cho lambda. Nếu các thành viên của việc đóng cửa sẽ không phải là 'const', thì tôi sẽ cần có 'mutable'-specifier trên lambda để truy cập hàm non-'const'. Tôi đã chỉ tự hỏi nếu có một cách để sao chép các biến 'const' như không 'const' vào đóng cửa. – z33ky

+0

@ z33ky, chụp nó hoàn toàn, sao chép rõ ràng :) '[&]() có thể thay đổi {Foo f (foo); f.Func(); }; ' – chill

+0

Tôi đang trả về lambda từ một hàm, do đó tham chiếu bị bắt sẽ bị vô hiệu. Tôi đoán tôi sẽ phải xây dựng một Funktor bản thân mình hoặc dựa vào 'const_cast'. Hoặc sống với một cuộc gọi ctor-ctor không cần thiết. – z33ky

0

Một workaround thể:

struct Foo 
    { 
     Foo(){} 
     void Func(){} 
    }; 

    int main() 
    { 
     const Foo foo; 
     { 
      Foo& fooo= const_cast<Foo&>(foo); 
      [fooo]() mutable { fooo.Func(); }; 
     } 
    } 

Giải pháp này có vấn đề về an toàn (sửa đổi ngẫu nhiên của đối tượng const thông qua tham chiếu không const có thể), nhưng sao chép thêm được tránh.

+0

Nếu điều này là cần thiết, có nghĩa là 'Func()' phải được đánh dấu 'const'. Nếu vậy, đây là một vấn đề riêng biệt từ lambda, vì vậy câu trả lời này, trong khi đúng, không thực sự là một giải pháp. (Một sẽ sửa 'Func()' thành cv-đủ điều kiện, hoặc nếu sửa đổi không thể cung cấp một hàm tự do 'void Func_unsafe (const Foo & f) {const_cast (f) .Func();}' và sử dụng – GManNickG

+0

GManNickG: Nếu Func thực sự sửa đổi đối tượng và được đánh dấu const, nó là vấn đề bảo mật toàn cầu cho toàn bộ chương trình, bất cứ khi nào const Foo xuất hiện. Bằng cách giới thiệu tham chiếu const-casted (thay vì làm cho const Func), tôi đã bản địa hóa vấn đề này bên trong khối xung quanh lambda, nơi tham chiếu const-casted cư trú. Khi fooo const-cast chỉ được sử dụng để tạo bản sao, hạn chế duy nhất là ctor sao chép của Foo không nên sửa đổi đối tượng (trường hợp thông thường). Không hoàn hảo, tôi đồng ý, nhưng nó là tốt hơn làm cho Func() const trên toàn cầu. – user396672

+0

GManNickG: ... Như void Func_unsafe(), nó lộ UB vì nó sửa đổi một đối tượng thực sự là const (tức là const member của lớp lambda) – user396672

Các vấn đề liên quan