Trong mã bên dưới, có hai lệnh "tương đương" với std::for_each
sử dụng các biểu thức boost:bind
. Biên dịch dòng được chỉ định, dòng thất bại được chỉ định không thành công. Lời giải thích tốt nhất tôi có thể tìm thấy trong số tiền tiêu chuẩn để "bởi vì chúng tôi đã nói như vậy". Tôi đang tìm "lý do tại sao tiêu chuẩn cho biết hành vi này". Giả định của tôi là dưới đây.boost :: liên kết với các thành viên và ngữ cảnh được bảo vệ
Câu hỏi của tôi đơn giản là: Tại sao biên dịch dòng được chỉ định và dòng tương ứng không biên dịch (và tôi không muốn vì "tiêu chuẩn nói như vậy", tôi đã biết điều đó - tôi sẽ không chấp nhận bất kỳ câu trả lời nào đưa ra giải thích này; Tôi muốn giải thích là lý do tại sao tiêu chuẩn nói như vậy).
Ghi chú: Mặc dù tôi sử dụng tăng, tăng không liên quan đến câu hỏi này và lỗi ở nhiều định dạng khác nhau đã được sao chép bằng g ++ 4.1. * Và VC7.1.
#include <boost/bind.hpp>
#include <iostream>
#include <map>
#include <algorithm>
class Base
{
protected:
void foo(int i)
{ std::cout << "Base: " << i << std::endl; }
};
struct Derived : public Base
{
Derived()
{
data[0] = 5;
data[1] = 6;
data[2] = 7;
}
void test()
{
// Compiles
std::for_each(data.begin(), data.end(),
boost::bind(&Derived::foo, this,
boost::bind(&std::map<int, int>::value_type::second, _1)));
// Fails to compile - why?
std::for_each(data.begin(), data.end(),
boost::bind(&Base::foo, this,
boost::bind(&std::map<int, int>::value_type::second, _1)));
}
std::map<int, int> data;
};
int main(int, const char**)
{
Derived().test();
return 0;
}
Dòng chỉ định thất bại với lỗi này: main.c: Trong chức năng thành viên 'khoảng trống nguồn gốc :: test()': main.c: 9: lỗi: 'khoảng trống cơ sở :: foo (int) 'được bảo vệ chính.C: 31: lỗi: trong ngữ cảnh này
Như đã nói, tuyên bố tương đương được cho là ở trên biên dịch sạch (và nếu tuyên bố vi phạm được nhận xét, chạy với kết quả mong đợi của việc in "5" , “6”, “7” trên các dòng riêng biệt).
Trong khi tìm kiếm một lời giải thích, tôi tình cờ gặp 11.5.1 trong tiêu chuẩn (cụ thể là, tôi nhìn vào dự thảo 2006/11/06):
An additional access check beyond those described earlier in clause 11 is applied when a non-static data member or nonstatic member function is a protected member of its naming class (11.2)105) As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall name C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.
Sau khi đọc bài viết này, nó trở thành rõ ràng tại sao câu lệnh thứ hai thất bại trong khi lần đầu tiên thành công, nhưng sau đó câu hỏi xuất hiện: Lý do cho điều này là gì? Ý tưởng ban đầu của tôi là trình biên dịch đã mở rộng các mẫu :: bound bind, phát hiện ra rằng Base :: foo đã được bảo vệ và khởi động nó bởi vì boost :: bind <…> không phải là một người bạn. Nhưng, tôi càng nghĩ về giải thích này, nó càng ít ý nghĩa, bởi vì nếu tôi nhớ chính xác, ngay khi bạn đưa con trỏ tới một thành viên (giả sử ban đầu bạn nằm trong sự kiểm soát truy cập của thành viên), mọi thông tin điều khiển truy cập là bị mất (tức là tôi có thể định nghĩa một hàm trả về một con trỏ tùy ý cho một thành viên mà trả về một thành viên công cộng, được bảo vệ hoặc riêng tư tùy thuộc vào một số đầu vào và người trả về sẽ không phải là người khôn ngoan hơn).
Tôi đã nghĩ về nó, và lời giải thích hợp lý duy nhất tôi có thể đưa ra lý do tại sao nó nên tạo sự khác biệt là trong trường hợp thừa kế nhiều lần. Cụ thể, tùy thuộc vào cách bố trí lớp, con trỏ thành viên khi được tính từ Cơ sở sẽ khác với con số được tính từ Nguồn gốc.
Tôi sẽ lưu ý ngay: điều này có thể cần một chủ đề tốt hơn. Nếu bất cứ ai có thể cho tôi một cái tốt hơn, tôi sẽ thay đổi nó. Cảm ơn trước. –
Vì tiêu chuẩn nói như vậy. –
Có vẻ như 'Base :: foo' phải có' virtual'. Nó có hoạt động nếu bạn thêm nó vào không? – zildjohn01