2013-04-15 49 views
7

tôi có mã này:std :: ràng buộc của chức năng thành viên lớp

#include <iostream> 
#include <functional> 

struct Foo 
{ 
     int get(int n) { return 5+n; } 
}; 

int main() 
{ 
     Foo foo; 
     auto L = std::bind(&Foo::get, &foo, 3); 

     std::cout << L() << std::endl; 

     return 0; 
} 

Có vẻ rằng đây:

auto L = std::bind(&Foo::get, &foo, 3); 

là equivalento tới:

auto L = std::bind(&Foo::get, foo, 3); 

Tại sao?

+8

Nó không phải. Một liên kết với một con trỏ, cái kia liên kết một bản sao *. – Xeo

+1

Đối với những gì nó có giá trị, bạn cũng có thể vượt qua một con trỏ thông minh (bất kỳ loại nào thực hiện 'toán tử->' để trả về 'foo *') làm đối số thứ hai. Hãy thử nó với một 'std :: shared_ptr'. –

+0

trùng lặp: http: //stackoverflow.com/questions/15264003/using-stdbind-with-member-function-use-object-pointer-or-not-for-this-argumen Mặc dù tôi thích cả hai câu trả lời ... – nephewtom

Trả lời

14

std::bind() chấp nhận đối số theo giá trị. Điều này có nghĩa là trong trường hợp đầu tiên bạn chuyển một con trỏ theo giá trị, dẫn đến bản sao của một con trỏ. Trong trường hợp thứ hai, bạn đang truyền một đối tượng kiểu foo theo giá trị, dẫn đến một bản sao của một đối tượng thuộc loại Foo.

Do đó, trong trường hợp thứ hai đánh giá của biểu thức L() làm cho viên chức năng get() được gọi trên bản sao của đối tượng gốc foo, mà có thể hoặc không thể là những gì bạn muốn.

Ví dụ này minh họa sự khác biệt (quên vi phạm các Rule của Ba/Rule of Five, điều này chỉ là dành cho mục đích minh hoạ):

#include <iostream> 
#include <functional> 

struct Foo 
{ 
    int _x; 

    Foo(int x) : _x(x) { } 

    Foo(Foo const& f) : _x(f._x) 
    { 
     std::cout << "Foo(Foo const&)" << std::endl; 
    } 

    int get(int n) { return _x + n; } 
}; 

int main() 
{ 
    Foo foo1(42); 

    std::cout << "=== FIRST CALL ===" << std::endl; 
    auto L1 = std::bind(&Foo::get, foo1, 3); 
    foo1._x = 1729; 
    std::cout << L1() << std::endl; // Prints 45 

    Foo foo2(42); 

    std::cout << "=== SECOND CALL ===" << std::endl; 
    auto L2 = std::bind(&Foo::get, &foo2, 3); 
    foo2._x = 1729; 
    std::cout << L2() << std::endl; // Prints 1732 
} 

Live example.

Nếu vì lý do nào đó, bạn không muốn sử dụng mẫu con trỏ, bạn có thể sử dụng std::ref() để ngăn chặn một bản sao của các đối số từ được tạo ra:

auto L = std::bind(&Foo::get, std::ref(foo), 3); 
3

Chúng không giống nhau. Chất kết dính chức năng chung std::bindbản sao đối số của nó. Trong trường hợp của std::bind(&Foo::get,&foo,3), con trỏ được sao chép, nhưng khi bạn gọi đối tượng liên kết, nó vẫn áp dụng cho đối tượng foo gốc. Trong std::bind(&Foo::get,foo,3) đối tượng foo được sao chép và cuộc gọi sau đó áp dụng cho bản sao bị ràng buộc, không áp dụng cho đối tượng gốc.

Bạn có thể kiểm tra điều này bằng cách sử dụng hàm thành viên truy cập trạng thái bên trong của đối tượng, liên kết đối tượng theo cả hai cách, thay đổi đối tượng gốc và xem kết quả khác nhau như thế nào.

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