2010-07-12 8 views
7

la elaboración de este código con g ++ 4.2.1:¿Clase base privada errónea inaccesible?

struct S { }; 
template<typename T> struct ST { }; 

template<typename BaseType> 
class ref_count : private BaseType { }; 

template<typename RefCountType> 
class rep_base : public RefCountType { }; 

class wrap_rep : public rep_base<ref_count<S> > { 
    typedef rep_base<ref_count<S> > base_type;  // line 11 
}; 

me sale:

bug.cpp:1: error: ‘struct S’ is inaccessible 
bug.cpp:11: error: within this context 

Sin embargo, si cambio de la clase wrap_rep utilizar ST:

class wrap_rep : public rep_base<ref_count< ST<int> > > { 
    typedef rep_base<ref_count< ST<int> > > base_type; 
}; 

que compila bien . Alternativamente, si cambio el código original a:

class wrap_rep : public rep_base<ref_count<S> > { 
    typedef rep_base<ref_count<::S> > base_type; // now using :: 
}; 

también compila bien. Para mí, el código original parece estar bien tal como está. ¿Es esto un error de g ++? Si no, ¿por qué funciona una plantilla? Y, para el otro caso, ¿por qué es necesario el ::S?

Respuesta

7

Ambos códigos no son válidos (solo el último es válido), pero el compilador (que no está conforme) solo diagnostica uno. Como dice otra respuesta, esto usa el nombre de clase inyectado. Se considera que una clase S tiene un nombre de miembro S que denota la misma clase. Por ejemplo (nótese la palabra clave "clase" antes S::S en el primer ejemplo es necesario forzar una referencia al nombre de la clase inyectada, en lugar del constructor por defecto):

class S { }; 

class S::S object; // creates an S object 
class X : S::S::S::S { }; // derives from class S 

plantillas de clase también tienen un nombre de clase inyectado. Al igual que el nombre de la clase inyectada, se hereda para las clases derivadas y, por lo tanto, ST<int> está mal formado porque usa ese nombre de clase inyectado, que sin embargo no es accesible. Si utiliza GCC menos 4,5, que puede tener algo que ver con una change introduced con GCC4.5:

G ++ ahora implementa DR 176. Anteriormente G ++ no admite el uso del nombre-clase-inyectada de una clase base de la plantilla como nombre de tipo, y la búsqueda del nombre encontró la declaración de la plantilla en el ámbito adjunto. Ahora la búsqueda del nombre encuentra el nombre de la clase inyectada, que se puede usar como tipo o como plantilla, dependiendo de si el nombre es seguido por una lista de argumentos de la plantilla. Como resultado de este cambio, un código que fue previamente aceptada podrá ser mal formada, porque

  1. El nombre-clase-inyectado no es accesible porque es a partir de una base privada, o
  2. El-clase- inyectada name no se puede usar como argumento para un parámetro de plantilla de plantilla.

En cualquiera de estos casos, el código se puede arreglar agregando un especificador de nombre anidado para nombrar explícitamente la plantilla. El primero se puede solucionar con -fno-access-control; el segundo solo se rechaza con -pedante.


tener un poco más divertido con los nombres de clase inyectados - Observe que el nombre de clase inyectado no es equivalente a un typedef como se podría pensar en primer lugar. El nombre de la clase que se inyecta es un nombre de clase, pero no está clasificado como un nombre de typedef, lo que significa que puede ser ocultada por función, objeto o empadronador nombres:

// valid, the data-member hides the injected class name 
struct S { int S; }; 

para referirse al nombre de la clase inyectada que pueda diga class S::S (del mismo modo, en una lista de clase base, los nombres que no son de tipo se ignoran, por lo que no necesita precausas especiales allí), pero una búsqueda simple en S::S se referirá al miembro de datos.

0

El código original compilado bien en "Sun WorkShop 6 update 2 Compilers C++". Este es el único al que tengo acceso en mi oficina. Puede probar con cualquier otro compilador que tenga.

3

Su struct S es una clase base de wrap_rep lo que significa que se ha inyectado en wrap_rep como si hubiera un typedef anónimo.

Usando el operador :: antes S en su typedef le dirá a su compilador no utilizar el S se hereda de, pero S en el espacio de nombres global.

Ver this link.

+0

Pero entonces, ¿por qué el código funciona con una clase base de plantilla? –

Cuestiones relacionadas