2010-09-07 6 views
5

Könnte mir jemand erklären, warum dieser Code:Weird-Compiler-Fehler und Vorlage Vererbung

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

Erzeugt den folgenden Compiler-Fehler?

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' 

Da beide safe_bool Vorlagen aus safe_bool_base ableiten, ich verstehe nicht, warum man nicht ein geschütztes Mitglied der Basisklasse zugreifen kann.

Fehle ich etwas?

+1

Das ist eine gute Frage. Ich schlage vor, dass Sie das Tag 'protected', 'base', 'derived' auch der Frage hinzufügen, damit dies in Suchen/Referenzen auftaucht. – Chubsdad

+0

@Chubsdad: Danke. Ich kann nur ein weiteres Tag hinzufügen. (5 ist die maximale Anzahl der Markierungen afaik.) – ereOn

Antwort

9

Dies sollte wahrscheinlich (reproduzierbar in einer nicht-Vorlage Situation auch) helfen

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

Für den gleichen Code, gibt Comeau

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

Hier ist der feste Code, der die gewünschten Absichten erreicht

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

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

int main(){ 
} 

Warum funktioniert das erste Code-Snippet nicht?

Dies ist wegen der folgenden Regel in der 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).

So die Rückkehr innerhalb Operatorfunktionen ändern

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 folgt: Bitte meine Erklärungen ignorieren. David hat Recht. Hier ist, worauf es ankommt.

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

Das klingt nach der richtigen Antwort! In der Idee, was die Logik hinter dieser Regel ist? –

+0

Sie sollten hervorheben "Wenn der Zugriff einen Zeiger auf Member bilden soll, muss der Nested-Name-Specifier die abgeleitete Klasse benennen", da dies ein Zeiger-zu-Mitglied ist. –

+0

@Steve Jessop: Yup, machte die Fettformatierung korrekt. Danke Freund – Chubsdad

1

Ich glaube nicht, dass dies etwas mit Vorlagen zu tun hat. Ihr Beispiel-Code kann auf diese reduziert werden, und es gibt immer noch die äquivalenten Fehler:

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


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

Ich glaube, das Problem ist, man kann nicht Mitglied-Funktionszeiger auf geschützte Elemente in der öffentlichen Schnittstelle zurückzukehren. (Edit: nicht wahr ...)

+0

Interessant. Aber wenn ich die letzte Template-Deklaration entferne, reklamiert der Compiler nicht und es gibt immer noch eine Methode, die die Adresse von 'safe_bool_base :: this_type_does_not_support_comparisons' zurückgibt. – ereOn

+0

Scheint logisch. Indem Sie eine Methode schützen, geben Sie an, dass Sie nur Ihren "Nachbarn" vertrauen wollen, die auf Ihre Schatzkiste zugreifen. In diesem Beispiel gibt der Nachbar einfach jedem den Schlüssel zur Schatzkiste (Zeiger auf geschützte Methode) – Patrick

+0

@Patrick: Ich stimme nicht zu. Wenn dies zutrifft, sollte dies auch für Member-Variablen gelten. Aber soweit ich weiß, hindert mich nichts daran, die Adresse einer 'privaten' oder' geschützten' Mitgliedsvariablen in einer öffentlichen Schnittstelle zurückzugeben. – ereOn

0

Die Antwort von Chubsdad klärt Ihre Frage, warum es einen Fehler für die Template-Spezialisierung gibt.

nun folgende C++ Standard-Regel

14.7.2/11 Die üblichen Regeln für den Zugang Kontrolle gelten nicht für die Namen verwendet, um explizite
instantiations
angeben. [Hinweis: Insbesondere können die in der Funktion
Deklarator (einschließlich Parametertypen, Rückgabetypen und Ausnahmespezifikationen) verwendeten Vorlagenargumente und -namen
private Typen oder Objekte sein, auf die normalerweise nicht zugegriffen werden kann, und die Vorlage kann eine
sein Elementvorlage oder Elementfunktion, auf die normalerweise nicht zugegriffen werden kann.- endnote]

würde erklären, warum die generische Vorlage Instanziierung würde keinen Fehler werfen. Es wird nicht geworfen, selbst wenn Sie einen privaten Zugriffsspezifizierer haben.