2010-09-07 15 views
5

Podría alguien explicar por qué este código:extraño error del compilador y la herencia de plantillas

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

produce el siguiente error de compilación?

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' 

Dado que ambas plantillas safe_bool derivan de safe_bool_base, no entiendo por qué no se puede acceder a un miembro protegido de la clase base.

¿Echo de menos algo?

+1

Esta es una buena pregunta. Te sugiero que agregues la etiqueta 'protected', 'base', 'derived' y la pregunta para que aparezca en las búsquedas/referencias – Chubsdad

+0

@Chubsdad: Thanks. Solo puedo agregar una etiqueta más. (5 es el número máximo de etiquetas afaik.) – ereOn

Respuesta

9

Esto probablemente debería ayudar a (reproducibles en una situación que no es plantilla tambié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'"

Por el mismo código, Comeau da

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

Aquí está el código fijo que logra las intenciones deseadas

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

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

int main(){ 
} 

Entonces, ¿por qué el primer fragmento de código no funciona?

Esto se debe a la siguiente regla en el 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).

Así que cambiar la devolución dentro de las funciones del operador de la siguiente manera

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: Por favor ignore mis explicaciones. David tiene razón Aquí es a lo que se reduce.

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

¡Parece la respuesta correcta! En una idea, ¿cuál es la razón de ser de esa regla? –

+0

Debe resaltar "Si el acceso es para formar un puntero al miembro, el especificador de nombre anidado nombrará la clase derivada", ya que este es un puntero a miembro. –

+0

@Steve Jessop: Sí, hizo el formato en negrita correctamente. Gracias amigo – Chubsdad

1

No creo que esto tenga nada que ver con las plantillas. El código de ejemplo puede reducirse a esto, y todavía da el error equivalente:

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


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

Creo que el problema es que no se puede volver punteros de función miembro a miembros protegidos en la interfaz pública. (Editar: no es cierto ...)

+0

Interesante. Pero si elimino la última declaración de la plantilla, el compilador no se queja y todavía hay un método que devuelve la dirección de 'safe_bool_base :: this_type_does_not_support_comparisons' – ereOn

+0

Parece lógico. Al proteger un método, usted indica que solo desea confiar en que sus "vecinos" accedan a su cofre del tesoro. En este ejemplo, el vecino simplemente le da a alguien la llave del cofre del tesoro (apunta al método protegido) – Patrick

+0

@Patrick: No estoy de acuerdo. Si esto fuera cierto, debería aplicarse a las variables miembro también. Pero hasta donde sé, nada me impide devolver la dirección de una variable de miembro 'privada' o' protegida' en una interfaz pública. – ereOn

0

La respuesta de Chubsdad aclara su pregunta de por qué hay un error en la especialización de la plantilla.

Ahora el siguiente C++ regla estándar

14.7.2/11 El acceso habitual comprobación de reglas no se aplican a los nombres utilizados para especificar explícitas
ejemplificaciones
. [Nota: En particular, los argumentos y nombres de plantilla utilizados en la función
declarator (incluidos los tipos de parámetros, tipos de devolución y especificaciones de excepción) pueden ser
tipos privados u objetos que normalmente no serían accesibles y la plantilla puede ser
plantilla de miembro o función miembro que normalmente no sería accesible.- Nota al final]

explicaría por qué la instanciación de plantilla genérica no arrojaría un error. No lanzará incluso si tiene un especificador de acceso privado.

Cuestiones relacionadas