2011-01-13 16 views
12

He encontrado algún código que I piensa debe compilar, pero no lo hace. Así que espero que algunos de los expertos en estándares locales aquí en SO puedan ayudar :-).Posible error de plantilla de g ++?

, básicamente, tienen un código que se parece a esto:

#include <iostream> 

template <class T = int> 
class A { 
public: 
    class U { 
    }; 

public: 
    U f() const { return U(); } 
}; 

// test either the work around or the code I want... 
#ifndef USE_FIX 
template <class T> 
bool operator==(const typename A<T>::U &x, int y) { 
    return true; 
} 
#else 
typedef A<int> AI; 
bool operator==(const AI::U &x, int y) { 
    return true; 
} 
#endif 

int main() { 
    A<int> a; 
    std::cout << (a.f() == 1) << std::endl; 
} 

Por lo tanto, para describir lo que está pasando aquí. Tengo una plantilla de clase (A) que tiene una clase interna (U) y al menos una función miembro que puede devolver una instancia de esa clase interna (f()).

Luego intento crear una función operator== que compare este tipo interno con otro tipo (en este caso, un int, pero no parece importar).

Cuando USE_FIX es no definido me sale el siguiente error:

test.cc: In function 'int main()': 
test.cc:27:25: error: no match for 'operator==' in 'a.A<T>::f [with T = int]() == 1' 

que parece extraño, porque yo soy claramente (creo) que define una plantilla operator== que debe cubrir esto, de hecho si sólo hacer un poco del trabajo para el compilador (habilitar USE_FIX), entonces ya no me sale un error. Desafortunadamente, la "corrección" no funciona genéricamente, solo para una instanciación específica de la plantilla.

¿Se supone que funciona como esperaba? ¿O simplemente esto no está permitido?

Por cierto: si es importante, estoy usando gcc 4.5.2.

+1

Simplemente no está permitido. Si supiera más sobre su problema, podría sugerir un rediseño apropiado. –

+0

+1 para una buena pregunta. :-) – Nawaz

+0

posible duplicado de [¿Cómo deducir el tipo de clase del tipo de método en las plantillas C++?] (Http://stackoverflow.com/questions/3830491/how-touceded-class-type-from-method-type- in-c-templates) –

Respuesta

15

El problema es que const typename A<T>::U &xU es un tipo dependiente y el compilador no se puede deducir a partir del argumento T (este es uno del contexto nondeduced).

Se podría, por ejemplo, tener dos especializaciones de A:

class X { }; 
class Y { }; 
class Z { }; 

template <> class A<X> { 
public: 
    typedef Z U; 
}; 

template <> class A<Y> { 
public: 
    typedef Z U; 
}; 

Si a continuación, llama a:

Z a; 
a == 1; 

lo que debería deducir el compilador T como? X? Y?

Una solución en este caso particular es declarar operator== como amigo nontemplate interior de la plantilla de clase:

template <class T = int> 
class A { 
public: 
    class U { 
    }; 

    friend bool operator==(const U& x, int y) { 
     return true; 
    } 

public: 
    U f() const { return U(); } 
}; 
+0

+1 para una buena respuesta a una buena pregunta! – Nawaz

+0

@James: por cierto, 'Z' debe declararse dentro de la plantilla de la clase, solo entonces su situación y su ejemplo se ajustarán. ¿Qué dices? Quiero decir, aquí 'Z' es independiente de' A', mientras que 'U' en la pregunta es de tipo dependiente. – Nawaz

+3

@Nawaz: no importa si 'Z' es dependiente; Importa que 'U' sea dependiente. 'Z' puede ser cualquier cosa. (Originalmente había usado 'int', pero estaba mal porque la plantilla de función ni siquiera se consideraría durante la resolución de sobrecarga debido al operador integrado, así que introduje' Z' como un tipo sin sobrecarga de plantilla 'operador' == '). –

11
template <class T> 
bool operator==(const typename A<T>::U &x, int y) { 
    return true; 
} 

Utilizando esta plantilla, no es permisible (o, a veces es posible) para deducir la parámetro de plantilla T del tipo x. Es lo que se conoce como un contexto no deducible. (Por ejemplo Alguien podría especializarse A para un parámetro diferente, digamos double y hacer A<double>::U un typedef para A<int>::U.)

No hay ninguna solución, tendría que especificar explícitamente el parámetro de plantilla que por operator== hace para la sintaxis feo.

+1

Creo que esta explicación es mejor que la explicación de James. Conciso y preciso. +1 – Nawaz

4

No está permitido por razones obvias. En general, no hay manera de que el compilador pueda deducir el argumento de la plantilla de su llamada al operador ==. Aparentemente, supusiste que el tipo anidado U define de manera única la especialización A adjunta. Eso no es cierto, que se puede ilustrar con el siguiente ejemplo con dos especializaciones explícitas de A

template <> class A<int> { 
public: 
    class U {}; 
}; 

template <> class A<double> { 
public: 
    typedef A<int>::U U; 
}; 

En este caso, si se llama con plantilla operador == con un argumento de tipo A<int>::U el compilador no puede deducir argumento de plantilla T de operador con plantilla ==. ¿Debe T ser int o double? No hay forma de decirlo.

Para evitar estas ambigüedades, tales situaciones se llaman contextos no deducidos. Deducir los argumentos de la plantilla de clase adjunta de un tipo anidado es un ejemplo de contexto no deducido.

+0

+1 para una buena respuesta a una buena pregunta! – Nawaz

Cuestiones relacionadas