2010-09-07 38 views
5

Có thể ai đó giải thích cho tôi tại sao mã này:Weird lỗi biên dịch và mẫu thừa kế

class safe_bool_base 
{ //13 
    protected: 

     typedef void (safe_bool_base::*bool_type)() const; 

     void this_type_does_not_support_comparisons() const {} //18 

     safe_bool_base() {} 
     safe_bool_base(const safe_bool_base&) {} 
     safe_bool_base& operator=(const safe_bool_base&) { return *this; } 
     ~safe_bool_base() {} 
}; 

template <typename T=void> class safe_bool : public safe_bool_base 
{ 
    public: 

     operator bool_type() const 
     { 
      return (static_cast<const T*>(this))->boolean_test() ? &safe_bool_base::this_type_does_not_support_comparisons : 0; 
     } 

    protected: 

     ~safe_bool() {} 
}; 

template <> class safe_bool<void> : public safe_bool_base 
{ 
    public: 

     operator bool_type() const 
     { 
      return (boolean_test() == true) ? &safe_bool_base::this_type_does_not_support_comparisons : 0; //46 
     } 

    protected: 

     virtual bool boolean_test() const = 0; 
     virtual ~safe_bool() {} 
}; 

Tạo lỗi biên dịch sau đây?

c:\project\include\safe_bool.hpp(46) : error C2248: 'safe_bool_base::this_type_does_not_support_comparisons' : cannot access protected member declared in class 'safe_bool_base' 
c:\project\include\safe_bool.hpp(18) : see declaration of 'safe_bool_base::this_type_does_not_support_comparisons' 
c:\project\include\safe_bool.hpp(13) : see declaration of 'safe_bool_base' 

Kể từ khi cả hai safe_bool mẫu lấy từ safe_bool_base, tôi không hiểu tại sao người ta không thể truy cập vào một thành viên bảo vệ của lớp cơ sở.

Tôi có thiếu gì đó không?

+1

Đây là một câu hỏi hay. Đề xuất bạn thêm thẻ 'được bảo vệ', 'cơ sở', 'có nguồn gốc' cũng như câu hỏi cho điều này để tìm kiếm/tham khảo – Chubsdad

+0

@Chubsdad: Cảm ơn. Tôi chỉ có thể thêm một thẻ nữa. (5 là số lượng thẻ tối đa được phép.) – ereOn

Trả lời

9

này nên có lẽ giúp (tái sản xuất trong một mẫu tình hình không còn)

struct A{ 
protected: 
    void f(){} 
}; 

struct B : A{ 
    void g(){&A::f;}  // error, due to Standard rule quoted below 
}; 

int main(){ 
} 

VS gives "'A::f' : cannot access protected member declared in class 'A'"

Đối với cùng một mã, Comeau cho

"ComeauTest.c", line 7: error: protected function "A::f" (declared at line 3) is not accessible through a "A" pointer or object void g(){&A::f;} ^

"ComeauTest.c", line 7: warning: expression has no effect void g(){&A::f;}

Dưới đây là mã cố định đạt được ý định mong muốn

struct A{ 
protected: 
    void f(){} 
}; 

struct B : A{ 
    void g(){&B::f;}  // works now 
}; 

int main(){ 
} 

Vì vậy, tại sao đoạn mã đầu tiên không hoạt động?

Điều này là do các quy tắc sau đây trong thư mục C++ Standard03

11.5/1- "When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

Vì vậy, thay đổi sự trở lại trong các chức năng điều hành như sau

return (boolean_test() == true) ? &safe_bool<void>::this_type_does_not_support_comparisons : 0; //46 

return (static_cast<const T*>(this))->boolean_test() ? &typename safe_bool<T>::this_type_does_not_support_comparisons : 0; 

EDIT 2: Xin vui lòng bỏ qua lời giải thích của tôi. David nói đúng. Đây là những gì nó sôi xuống.

struct A{ 
protected: 
    int x; 
}; 

struct B : A{ 
    void f(); 
}; 

struct C : B{}; 

struct D: A{   // not from 'C' 
}; 

void B::f(){ 
    x = 2;   // it's own 'A' subobjects 'x'. Well-formed 

    B b; 
    b.x = 2;  // access through B, well-formed 

    C c; 
    c.x = 2;  // access in 'B' using 'C' which is derived from 'B', well-formed. 

    D d; 
    d.x = 2;  // ill-formed. 'B' and 'D' unrelated even though 'A' is a common base 
} 

int main(){} 
+0

Điều này nghe giống như câu trả lời đúng! Ý tưởng về lý do đằng sau quy tắc đó là gì? –

+0

Bạn nên đánh dấu "Nếu truy cập là để tạo thành một con trỏ đến thành viên, thì tên lồng nhau-specifier sẽ đặt tên cho lớp dẫn xuất", vì đây là một con trỏ tới thành viên. –

+0

@Steve Jessop: Yup, đã tạo định dạng in đậm chính xác. Cảm ơn bạn thân – Chubsdad

1

Tôi không nghĩ rằng đây là bất cứ điều gì liên quan đến mẫu. mã ví dụ bạn có thể được giảm xuống này, và nó vẫn cung cấp cho các lỗi tương đương:

class A 
{ 
    protected: 
     typedef void (A::*type)() const; 
     void foo() const {} 
}; 


class B : public A 
{ 
    public: 
     operator type() const 
     { 
      return &A::foo; 
     } 
}; 

Tôi tin rằng vấn đề là bạn không thể trở về con trỏ thành viên-chức năng để bảo vệ các thành viên trong giao diện công cộng. (Edit: không đúng sự thật ...)

+0

Thú vị. Nhưng nếu tôi loại bỏ khai báo mẫu cuối cùng, trình biên dịch không phàn nàn và vẫn có một phương thức trả về địa chỉ 'safe_bool_base :: this_type_does_not_support_comparisons' – ereOn

+0

Có vẻ hợp lý. Bằng cách thực hiện một phương pháp bảo vệ bạn chỉ ra rằng bạn chỉ muốn tin tưởng 'hàng xóm' của bạn truy cập vào kho báu của bạn. Trong ví dụ này, người hàng xóm chỉ đơn giản là cung cấp cho bất cứ ai chìa khóa để kho tàng ngực (con trỏ đến phương pháp bảo vệ) – Patrick

+0

@ Patrick: Tôi không đồng ý. Nếu điều này đúng, nó cũng nên áp dụng cho các biến thành viên. Nhưng theo như tôi biết, không có gì ngăn cản tôi trả về địa chỉ của biến thành viên 'private' hoặc' protected' trong một giao diện công cộng. – ereOn

0

Câu trả lời của Chubsdad làm rõ câu hỏi của bạn tại sao có lỗi cho chuyên môn mẫu.

Bây giờ C++ sau quy tắc tiêu chuẩn

14.7.2/11 Việc tiếp cận thông thường kiểm tra quy tắc không áp dụng cho tên được sử dụng để xác định rõ ràng
instantiations
. [Lưu ý: Cụ thể, các đối số mẫu và tên được sử dụng trong hàm
người khai báo (bao gồm loại thông số, loại trả về và thông số ngoại lệ) có thể là
các loại hoặc đối tượng riêng thường không thể truy cập được và mẫu có thể là
mẫu thành viên hoặc chức năng thành viên thường không thể truy cập được.- endnote]

sẽ giải thích lý do tại sao bản mẫu chung sẽ không gây ra lỗi. Nó sẽ không ném ngay cả khi bạn có thông số truy cập riêng tư.

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