2016-05-16 25 views
5

Tôi đang bối rối về lý do tại sao std::mem_fn là cần thiết.Tại sao sử dụng mem_fn?

Tôi có chức năng nhận bất kỳ cuộc gọi nào (lambda, con trỏ hàm, v.v.) và liên kết nó với đối số.

Ví dụ:

template<class T> 
void Class::DoBinding(T callable) { 
    m_callable = std::bind(callable, _1, 4); 
} 
//somewhere else 
Item item; 
m_callable(item); 

Tất cả các mẫu mã tôi đã nhìn thấy làm:

//some defined member function 
Item::Foo(int n); 

DoBinding(std::mem_fn(&Item::Foo)); 

Tại sao không thể nó chỉ đơn giản là:

DoBinding(&Item::Foo); 

Có vẻ như sau này là callable mà không cần phải sử dụng std :: mem_fn, vậy tại sao nó lại cần thiết?

+1

Không, bạn không cần 'mem_fn' cho điều đó. Bạn cần nó cho những thứ khác. –

+1

Bạn không bao giờ nên chuyển đến 'std :: bind' một giá trị của một loại không xác định. Nếu kiểu đó là một biểu thức ràng buộc, thì hành vi đó là đáng ngạc nhiên. Nói cách khác, 'std :: bind' nên được sử dụng trong mã lá, không phải trong mã chung. –

+0

Điều gì về việc sử dụng lambda thay vì std :: bind? Tôi cảm thấy rằng cú pháp dễ đọc hơn và khi bạn sử dụng C++ 14, nó sẽ bao gồm tất cả (gần như tất cả?) Các trường hợp sử dụng tương tự mà ràng buộc có – AndyG

Trả lời

8

Điều này thường được thực hiện vì người viết DoBinding(std::mem_fn(&Item::Foo)) không có ý tưởng rằng DoBinding có thể lấy trực tiếp con trỏ thành viên.

Hãy nhớ: std::sort(..., &Item::Foo) sẽ không, vì sort hy vọng giá trị là đối tượng hàm có thể gọi trực tiếp. Và các con trỏ thành viên không phải là. Thật vậy, khá nhiều thuật toán trong thư viện chuẩn C++ sẽ thất bại khi được đưa ra một con trỏ thành viên thay vì kiểu trực tiếp có thể gọi được. DoBinding của bạn chỉ hoạt động vì bạn đang sử dụng std::bind, có quá tải đặc biệt cho các con trỏ thành viên. Người gọi của DoBinding không nhất thiết phải biết bạn đang làm điều đó.

Hầu hết mã nhận cuộc gọi theo thông số mẫu sẽ bị ngắt trên con trỏ thành viên. Vì vậy, để được an toàn, chúng tôi không vượt qua con trỏ thành viên xung quanh là đối tượng có thể được gọi trực tiếp; sử dụng mem_fn để biến nó thành một đối tượng như vậy.

10

Điều này là do mã chung mong đợi UnaryFunction hoặc BinaryFunction sẽ gọi trực tiếp mã đó bằng cú pháp gọi thông thường. Vì vậy, để chọn một thuật toán độc đoán như for_each, nó có thể cũng được thực hiện như:

template<class InputIt, class UnaryFunction> 
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f) 
{ 
    for (; first != last; ++first) { 
     f(*first); // <== N.B. f(*first) 
    } 
    return f; 
} 

Nếu bạn gọi for_each() với &Item::Foo, mã cố gắng gọi (&Item::Foo)(x), mà là vô hình thành kể từ khi cho con trỏ tới các thành viên, bạn phải viết (x.*&Item::Foo)(). Đó là sự khác biệt về cú pháp mà mem_fn có nghĩa là để giải quyết: mem_fn giải quyết cú pháp gọi của các con trỏ đến các thành viên để bạn có thể sử dụng tất cả các thuật toán với con trỏ tới các thành viên cũng như các hàm và đối tượng hàm. Bạn không thể có for_each(v.begin(), v.end(), &Item::Foo) nhưng bạn có thể có for_each(v.begin(), v.end(), mem_fn(&Item::Foo)).

Điều này chỉ hoạt động tốt trong std::bind() (và std::threadstd::function và ...) vì tất cả những điều này đều có cách xử lý rõ ràng cho con trỏ đến các thành viên riêng biệt. Và kể từ khi DoBinding() chính nó gọi std::bind(), không có lý do nào cho std::mem_fntrong trường hợp này là.


Có đề xuất để loại bỏ sự khác biệt cú pháp này: P0312.

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