2010-09-07 19 views
5

mógłby ktoś wytłumaczyć mi, dlaczego ten kod:Weird błąd kompilatora i szablon dziedziczenie

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

Wytwarza się następujący błąd kompilatora?

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' 

Ponieważ obie safe_bool szablony pochodzą z safe_bool_base, ja nie rozumiem, dlaczego nie można uzyskać dostęp do chronionego członka klasy bazowej.

Czy brakuje mi czegoś?

+1

To jest dobre pytanie. Sugerujemy dodanie tagu "protected", "base", "deriv" ​​oraz pytanie, które będzie pojawiać się w zapytaniach/odnośnikach. – Chubsdad

+0

@Chubsdad: Dzięki. Mogę dodać tylko jeszcze jeden tag. (5 to maksymalna liczba tagów afaik.) – ereOn

Odpowiedz

9

to prawdopodobnie powinien pomóc (powtarzalne w nie także sytuacji szablonu)

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

Z tego samego kodu, Comeau daje

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

Oto stały kod, który osiąga pożądane intencje

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

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

int main(){ 
} 

Dlaczego pierwszy fragment kodu nie działa?

Wynika to z następującej reguły w 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).

Więc zmienić zwrot w funkcji operatorskich następująco

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: Proszę ignorować moje wyjaśnienia. David ma rację. Oto, do czego to się sprowadza.

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

To brzmi jak odpowiednia odpowiedź! Jaki jest sens tej reguły? –

+0

Powinieneś podświetlić "Jeśli dostęp ma utworzyć wskaźnik do elementu, specyfikator nazwy zagnieżdżonej powinien nadać nazwę klasie pochodnej", ponieważ jest to wskaźnik do członka. –

+0

@Steve Jessop: Tak, poprawnie wytłuszczono formatowanie. Dzięki, kolego – Chubsdad

1

Nie sądzę, że ma to coś wspólnego z szablonami. Twój przykładowy kod może być zredukowana do tego, a to wciąż daje równoważną błąd:

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


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

Uważam, że problemem jest to, że nie może wrócić wskazówek członków funkcyjnych członków chronionych w interfejsie publicznym. (Edit: nie prawda ...)

+0

Interesujące. Ale jeśli usuwam ostatnią deklarację szablonu, kompilator nie narzeka i nadal istnieje metoda zwracająca adres 'safe_bool_base :: this_type_does_not_support_comparisons' – ereOn

+0

Wydaje się logiczne. Tworząc chronioną metodę, oznaczasz, że chcesz tylko zaufać swoim "sąsiadom", którzy mają dostęp do twojej skrzyni skarbów. W tym przykładzie sąsiad po prostu daje każdemu klucz do skrzyni skarbów (wskaźnik do metody chronionej) – Patrick

+0

@Patrick: Nie zgadzam się. Jeśli to prawda, powinna również odnosić się do zmiennych członkowskich. Ale o ile mi wiadomo, nic nie stoi na przeszkodzie, aby zwrócić adres zmiennej prywatnej "private" lub "protected" w interfejsie publicznym. – ereOn

0

Odpowiedź Chubsdada wyjaśnia pytanie o przyczynę błędu w specjalizacji szablonu.

teraz następujące C++ standardowy przepis

14.7.2/11 Zwykła kontrola dostępu zasady nie mają zastosowania do nazw używanych do określenia wyraźnych
dawałaby
. [Uwaga: W szczególności argumenty i nazwy szablonów używane w deklaratorze funkcji (w tym typy parametrów, typy zwrotów i specyfikacje wyjątków) mogą być typami prywatnymi lub obiektami, które normalnie nie byłyby dostępne, a szablonem może być
Szablon członka lub funkcja członka, która normalnie nie byłaby dostępna.- endnote]

wyjaśniłoby, dlaczego ogólne tworzenie szablonu nie spowodowałoby błędu. Nie wyrzuci, nawet jeśli posiadasz specyfikator dostępu prywatnego.