Đây là một câu hỏi đơn giản nhưng câu trả lời là phức tạp đáng ngạc nhiên. Câu trả lời ngắn gọn là bạn có thể làm những gì bạn đang cố gắng làm với std :: bind1st hoặc boost :: bind. Câu trả lời dài hơn là dưới đây.
Trình biên dịch chính xác để đề xuất bạn sử dụng & CLoggersInfra :: RedundencyManagerCallBack. Đầu tiên, nếu RedundencyManagerCallBack là một hàm thành viên, chính hàm đó không thuộc về bất kỳ cá thể cụ thể nào của lớp CLoggersInfra. Nó thuộc về chính lớp đó. Nếu bạn đã từng gọi một hàm lớp tĩnh trước đây, bạn có thể đã nhận thấy rằng bạn sử dụng cùng cú pháp SomeClass :: SomeMemberFunction. Vì bản thân hàm là 'tĩnh' theo nghĩa là nó thuộc về lớp chứ không phải là một cá thể cụ thể, bạn sử dụng cùng một cú pháp. '&' là cần thiết bởi vì về mặt kỹ thuật nói rằng bạn không chuyển trực tiếp các hàm - các hàm không phải là các đối tượng thực trong C++. Thay vào đó, bạn về mặt kỹ thuật truyền địa chỉ bộ nhớ cho hàm, đó là, một con trỏ trỏ đến nơi các lệnh của hàm bắt đầu trong bộ nhớ. Hệ quả là như nhau, bạn có hiệu quả 'truyền một hàm' như một tham số.
Nhưng đó chỉ là một nửa vấn đề trong trường hợp này. Như tôi đã nói, RedundencyManagerCallBack chức năng không 'thuộc' đối với bất kỳ cá thể cụ thể nào. Nhưng có vẻ như bạn muốn truyền nó như một cuộc gọi lại với một ví dụ cụ thể trong đầu. Để hiểu cách thực hiện điều này, bạn cần hiểu các hàm thành viên thực sự là gì: các hàm thường xuyên không được định nghĩa trong bất kỳ lớp nào có tham số ẩn thêm.
Ví dụ:
class A {
public:
A() : data(0) {}
void foo(int addToData) { this->data += addToData; }
int data;
};
...
A an_a_object;
an_a_object.foo(5);
A::foo(&an_a_object, 5); // This is the same as the line above!
std::cout
Có bao nhiêu thông số không A :: foo mất? Thông thường chúng tôi sẽ nói 1. Nhưng dưới mui xe, foo thực sự mất 2. Nhìn vào định nghĩa của A :: foo, nó cần một ví dụ cụ thể của A để con trỏ 'this' có ý nghĩa (trình biên dịch cần biết cái gì ' đây là). Cách bạn thường chỉ định những gì bạn muốn 'này' được thông qua cú pháp MyObject.MyMemberFunction(). Nhưng đây chỉ là cú pháp cú pháp để truyền địa chỉ MyObject như tham số đầu tiên cho MyMemberFunction. Tương tự như vậy khi chúng ta khai báo các hàm thành viên bên trong các định nghĩa lớp, chúng ta không đặt 'this' trong danh sách tham số, nhưng đây chỉ là một món quà từ các nhà thiết kế ngôn ngữ để lưu kiểu gõ. Thay vào đó, bạn phải chỉ định rằng một hàm thành viên là tĩnh để chọn không tham gia nó tự động nhận thêm tham số 'this' này. Nếu ++ biên dịch C dịch ví dụ trên để mã C (bản gốc C++ làm việc thực tế như vậy), nó có lẽ sẽ viết một cái gì đó như thế này:
struct A {
int data;
};
void a_init(A* to_init)
{
to_init->data = 0;
}
void a_foo(A* this, int addToData)
{
this->data += addToData;
}
...
A an_a_object;
a_init(0); // Before constructor call was implicit
a_foo(&an_a_object, 5); // Used to be an_a_object.foo(5);
Quay lại ví dụ của bạn, hiện nay là một vấn đề rõ ràng. 'Init' muốn một con trỏ trỏ đến một hàm nhận một tham số. Nhưng & CLoggersInfra :: RedundencyManagerCallBack là một con trỏ tới một hàm nhận hai tham số, đó là tham số bình thường và tham số bí mật 'this'. Vì vậy, tại sao bạn vẫn nhận được một lỗi trình biên dịch (như một lưu ý phụ: Nếu bạn đã từng sử dụng Python, loại nhầm lẫn này là lý do tại sao một tham số 'tự' là cần thiết cho tất cả các hàm thành viên).
Cách tiết kiệm để xử lý việc này là tạo đối tượng đặc biệt chứa con trỏ đến cá thể bạn muốn và có hàm thành viên được gọi là 'chạy' hoặc 'thực thi' (hoặc quá tải toán tử '()') lấy các tham số cho hàm thành viên và chỉ cần gọi hàm thành viên với các tham số đó trên cá thể được lưu trữ. Nhưng điều này sẽ yêu cầu bạn thay đổi 'Init' để lấy đối tượng đặc biệt của bạn chứ không phải là một con trỏ hàm thô, và có vẻ như Init là mã của người khác. Và tạo ra một lớp đặc biệt cho mỗi lần vấn đề này xuất hiện sẽ dẫn đến mã bloat.
Vì vậy, bây giờ, cuối cùng, giải pháp tốt, đẩy mạnh :: ràng buộc và tăng :: chức năng, tài liệu cho mỗi bạn có thể tìm thấy ở đây:
boost::bind docs, boost::function docs
boost :: ràng buộc sẽ cho phép bạn có một hàm, và một tham số cho hàm đó, và tạo một hàm mới trong đó tham số đó được 'khóa' tại chỗ. Vì vậy, nếu tôi có một hàm có thêm hai số nguyên, tôi có thể sử dụng boost :: bind để tạo một hàm mới trong đó một trong các tham số bị khóa để nói 5. Hàm mới này sẽ chỉ lấy một tham số nguyên và sẽ luôn thêm 5 cho nó. Sử dụng kỹ thuật này, bạn có thể 'khóa' tham số ẩn 'này' thành một cá thể lớp cụ thể và tạo một hàm mới chỉ lấy một tham số, giống như bạn muốn (lưu ý rằng tham số ẩn luôn là đầu tiên và tham số bình thường theo thứ tự sau nó). Nhìn vào boost :: bind docs cho các ví dụ, họ thậm chí còn thảo luận cụ thể về việc sử dụng nó cho các hàm thành viên. Về mặt kỹ thuật, có một hàm chuẩn được gọi là std :: bind1st mà bạn có thể sử dụng, nhưng boost :: bind là tổng quát hơn.
Tất nhiên, chỉ còn một lần nữa. boost :: bind sẽ tạo ra một hàm boost :: tốt cho bạn, nhưng đây vẫn là kỹ thuật không phải là một con trỏ hàm thô như Init có thể muốn. Rất may, boost cung cấp một cách để chuyển đổi boost :: function's thành con trỏ thô, như được ghi trên StackOverflow here. Làm thế nào nó thực hiện điều này là vượt ra ngoài phạm vi của câu trả lời này, mặc dù nó thú vị quá.
Đừng lo lắng nếu điều này có vẻ lố bịch - câu hỏi của bạn cắt một số góc tối hơn của C++ và tăng :: liên kết cực kỳ hữu ích khi bạn tìm hiểu.
Liên kết hiện là https://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr; có vẻ như bây giờ anh ta nói "Đừng". Đây là lý do tại sao các câu trả lời chỉ có liên kết là không tốt. –
Hãy chỉnh sửa câu trả lời 8 tuổi của tôi :-) –
Oh yeah ... duh :-) –