2010-09-07 97 views
5

有人能解釋我爲什麼這個代碼:奇怪的編譯器錯誤和模板繼承

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() {} 
}; 

產生如下的編譯器錯誤?

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' 

由於兩個safe_bool模板從safe_bool_base派生,我不明白爲什麼人們不能訪問基類的保護成員。

我錯過了什麼嗎?

+1

這是一個很好的問題。建議您添加標記'受保護','基','派生'以及這個問題在搜索/參考 – Chubsdad 2010-09-07 10:29:13

+0

@Chubsdad:謝謝。我只能添加一個標籤。 (5是最大標籤數afaik。) – ereOn 2010-09-07 11:35:23

回答

9

這也許應該幫助(在非模板的情況也可重複)

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'"

對於相同的代碼,科莫給

"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;}

這是固定的代碼,它實現了所需的意圖

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

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

int main(){ 
} 

那麼,爲什麼第一個代碼片段不起作用?

這是因爲在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).

以下規則的改變如此操作功能內的回報如下

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; 

編輯2:請忽略我的解釋。大衛是對的。這是它歸結爲什麼。

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

這聽起來像是正確的答案!在想法這個規則背後的理由是什麼? – 2010-09-07 10:12:33

+0

你應該突出顯示「如果訪問是形成一個指向成員的指針,嵌套名稱說明符應該命名派生類」,因爲這是一個指向成員的指針。 – 2010-09-07 10:14:32

+0

@Steve Jessop:是的,做了正確的粗體格式。感謝好友 – Chubsdad 2010-09-07 10:16:26

1

我不認爲這與模板有關。您的示例代碼可以減少此,它仍然給出了相當於錯誤:

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


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

我相信這個問題是你不能在公共接口成員函數指針返回protected成員。編輯:不是真的...

+0

有趣。但是,如果我刪除最後一個模板聲明,編譯器不會抱怨,並且仍然有一個方法返回'safe_bool_base :: this_type_does_not_support_comparisons' – ereOn 2010-09-07 09:53:22

+0

的地址似乎合乎邏輯。通過提供一種保護方法,您可以表明您只希望相信您的「鄰居」訪問您的寶箱。在這個例子中,鄰居只是給任何人的寶箱(指向受保護的方法)的鑰匙 – Patrick 2010-09-07 09:55:17

+0

@帕特里克:我不同意。如果這是真的,它也應該適用於成員變量。但據我所知,沒有任何東西阻止我在公共接口中返回「private」或「protected」成員變量的地址。 – ereOn 2010-09-07 09:57:07

0

Chubsdad的回答澄清了爲什麼模板專業化存在錯誤的問題。

現在下面的C++標準規則

14.7.2/11 通常的訪問檢查規則並不適用於用來指定明確的
實例
名。 [注意:特別是,函數
聲明符(包括參數類型,返回類型和異常規格)中使用的模板參數和名稱可能是
通常不可訪問的私有類型或對象,模板可能是
成員模板或成員函數通常無法訪問。 - 尾註]

將解釋爲什麼泛型模板實例化不會拋出錯誤。即使你有私人訪問說明符也不會拋出。