2010-12-13 39 views
16

Khi sử dụng các hàm lambda, giả sử bạn quyết định sao chép một biến (với ký hiệu [=]). Nếu bạn không bao giờ tham chiếu biến đó nữa, trình biên dịch có được phép di chuyển nó vào đối tượng hàm kết quả không?Di chuyển với lambdas

Chỉnh sửa: Ví dụ: tôi đã viết một đoạn mã để di chuyển cuộc gọi qua các chuỗi. Đây là một mẫu làm như vậy.

extern "C" __declspec(dllexport) void parser_file_updated(Parser* p, const char* filename, int offset, int added) { 
    std::string file(filename); 
    p->make_call([=]() { 
     p->file_updated(std::move(file), offset, added); 
    }); 
} 

Nhưng rõ ràng, biến tệp không cần phải sống qua định nghĩa lambda- và thực sự, lambda chỉ được gọi một lần, vì vậy tôi đã chuyển bản sao.

+3

Tôi đoán rằng điều này là quá khó để tìm ra nói chung cho trình biên dịch. Bạn có thể cung cấp mã ví dụ không? – fredoverflow

+0

Có quy tắc "như thể" tất nhiên. Nhưng tôi đoán bạn đang nghĩ về một trường hợp mà các ctor sao chép và/hoặc di chuyển ctor có tác dụng phụ, vì vậy bạn có thể nói sự khác biệt. Câu hỏi thú vị. – aschepler

+0

@aschepler: nếu tôi nhớ chính xác, trình biên dịch được phép bỏ qua các tác dụng phụ của các nhà xây dựng sao chép và di chuyển. @ DeadMG: Hình như tôi có thể tối ưu hóa nó đi, bạn đã thử chưa? –

Trả lời

12

Nếu bạn không bao giờ tham khảo biến đó nữa, trình biên dịch có được phép di chuyển nó vào đối tượng hàm kết quả không?

No. Tình huống duy nhất mà trình biên dịch được phép thay thế bản sao bằng cử động là các tình huống tương tự chính xác khi được phép thực hiện một bản sao chép. Những tình huống này bao gồm trả về một đối tượng cục bộ theo giá trị hoặc khởi tạo một đối tượng với một tạm thời. Trong những trường hợp này, trình biên dịch được phép bỏ qua bản sao bằng cách tạo nguồn và nhắm mục tiêu cùng một đối tượng. Nếu trình biên dịch không thể thực hiện điều đó vì bất kỳ lý do nào, nó phải xem xét đối tượng nguồn như là một rvalue đối với độ phân giải quá tải để chọn hàm tạo thích hợp cho đối tượng đích. Trong trường hợp của bạn, tuy nhiên, tập tin là một Lvalue và không ai trong số các trường hợp từ trên áp dụng. Bạn sẽ phải sử dụng một động thái rõ ràng.

Thật không may, C++ 11 không có cú pháp cho "di chuyển chụp". IMHO, thật đáng tiếc. Nhưng std :: bind hỗ trợ điều này. Bạn có thể kết hợp std :: bind với một biểu thức lambda như sau:

void foo(char const* p) { 
    string s = p; 
    auto fun = bind([](string const& s){ 
     ... 
    },move(s)); 
    fun(); 
} 

để chuỗi được di chuyển vào đối tượng hàm.

Nếu bạn có ý định gọi chức năng này chỉ một lần và muốn di chuyển chuỗi ra khỏi đối tượng chức năng một lần nữa, bạn có thể sử dụng một tham chiếu không const:

void foo(char const* p) { 
    string s = p; 
    auto fun = bind([](string & s) { 
     some_other_func(move(s)); 
    },move(s)); 
    fun(); 
} 

Lưu ý rằng, nếu bạn không muốn sử dụng ràng buộc ở đây nhưng chúng ta hãy xây dựng các đối tượng lambda tạo một bản sao của s, di chuyển các chuỗi ra khỏi đối tượng chức năng đòi hỏi các từ khóa có thể thay đổi:

void foo(char const* p) { 
    string s = p; 
    auto fun = [=]() mutable { 
     //   ^^^^^^^ 
     some_other_func(move(s)); 
    }; 
    fun(); 
} 

bởi vì nếu không điều hành kiểu đóng cửa() chức năng sẽ const- đủ điều kiện mà lần lượt làm cho s một chuỗi có trình độ const.

Trong C++ 14, điều khoản chụp lambda linh hoạt hơn một chút. Bây giờ chúng ta có thể viết

void foo(char const* p) { 
    string s = p; 
    auto fun = [s=move(s)]() mutable { // #1 
     some_other_func(move(s));  // #2 
    }; 
    fun(); 
} 

nơi # 1 di chuyển các chuỗi giá trị vào đối tượng lambda và # 2 di chuyển các chuỗi giá trị ra (tùy thuộc vào cách some_other_func được khai báo chính xác).

+0

Trình biên dịch C++ có thể thực hiện bất kỳ sự tối ưu nào mà nó cảm thấy, miễn là hành vi quan sát là như nhau. Trong trường hợp cụ thể này, nó có thể di chuyển đối tượng mà bạn không thể nói, vì vậy nó được phép. Điều này làm cho câu trả lời của bạn thực sự sai. – nwp

+0

@nwp: Đối với tất cả ý định và mục đích, tôi không đồng ý. Hành vi quan sát bao gồm thực tế cho dù một ctor bản sao hoặc một ctor di chuyển được gọi - giả sử các hàm hoạt động khác nhau - đó là, tất nhiên, mục đích của một ctor di chuyển. Quy tắc as-if không giúp bạn giảm chi phí tại đây. – sellibitze