2009-07-22 10 views
8

tengo el siguiente código (lo siento por el gran trozo de código, pero no podía reducirlo más)plantilla ambigua rareza

template <bool B> 
struct enable_if_c { 
     typedef void type; 
}; 

template <> 
struct enable_if_c<false> {}; 

template <class Cond> 
struct enable_if : public enable_if_c<Cond::value> {}; 

template <typename X> 
struct Base { enum { value = 1 }; }; 

template <typename X, typename Y=Base<X>, typename Z=void> 
struct Foo; 

template <typename X> 
struct Foo<X, Base<X>, void> { enum { value = 0 }; }; 

template <typename X, typename Y> 
struct Foo<X, Y, typename enable_if<Y>::type > { enum { value = 1 }; }; 

int main(int, char**) { 
     Foo<int> foo; 
} 

Pero falla al compilar con gcc (v4.3) con

foo.cc: In function ‘int main(int, char**)’: 
foo.cc:33: error: ambiguous class template instantiation for ‘struct Foo<int, Base<int>, void>’ 
foo.cc:24: error: candidates are: struct Foo<X, Base<X>, void> 
foo.cc:27: error:     struct Foo<X, Y, typename enable_if<Y>::type> 
foo.cc:33: error: aggregate ‘Foo<int, Base<int>, void> foo’ has incomplete type and cannot be defined 

Bien, entonces es ambiguo. pero no esperaba que fuera un problema, ya que al usar la especialización casi siempre habrá alguna ambigüedad. Sin embargo, este error solo se desencadena al usar la clase con enable_if<...>, si lo reemplazo con una clase como la siguiente no hay problema.

template <typename X, typename Y> 
struct Foo<X, Y, void > { enum { value = 2 }; }; 

¿Por qué esta clase no causa una ambigüedad mientras que las otras sí? ¿No son los dos lo mismo para las clases con un verdadero :: valor? De todos modos, cualquier sugerencia sobre lo que estoy haciendo mal se aprecia.

Gracias por las respuestas, mi problema real (para obtener el compilador para seleccionar la primera especialización) se resolvió mediante la sustitución de struct Foo<X, Base<X>, void> con struct Foo<X, Base<X>, typename enable_if< Base<X> >::type > que parece funcionar de la manera que quiero.

+0

El editor de rebajas no es wysiwyg con respecto a

+0

Lo arreglé antes, sobrescribió mis cambios :) Si lo desea, puede volver a mi edición (utilicé el botón "10101", que marca automáticamente las cosas como código, debe usar ese botón también). –

+0

Uhjm ... Lo hice de nuevo sin leer tu comentario litb –

Respuesta

12

La esencia de su pregunta es que usted tiene:

template <typename X, typename Y, typename Z> 
struct Foo {}; 

template <typename X> 
struct Foo<X, Base<X>, void> {};     // #1 

template <typename X, typename Y> 
struct Foo<X, Y, typename whatever<Y>::type> {}; // #2 

y que está tratando de coincidir con

Foo<int, Base<int>, void> 

Obviamente, ambos coincidan especializaciones (el primero con X = int, el segundo con X = int, Y = Base<int>).

Según la norma, sección 14.5.4, si hay más especializaciones coincidentes, se construye una ordenación parcial (como se define en 14.5.5.2) entre ellas y se utiliza la más especializada. En su caso, sin embargo, ninguno es más especializado que el otro. (En pocas palabras, una plantilla es más especializada que otra, si puede reemplazar cada tipo de parámetro de la última plantilla con algún tipo y obtener la firma de la anterior. Además, si tiene whatever<Y>::type y reemplaza Y con Base<X>, obtiene whatever<Base<X> >::type no void, es decir, no hay tratamiento que se realizan.)

Si reemplaza #2 con

template <typename X, typename Y> 
struct Foo<X, Y, void > {};      // #3 

entonces el candidato ajustar de nuevo contiene ambas plantillas, sin embargo, # 1 es más especializado a continuación, # 3 y, como tal, está seleccionado.

+0

esto responde mi pregunta, pero mi problema principal sigue siendo. No sé cómo se supone que se deben hacer las preguntas de seguimiento aquí, pero voy a intentar un comentario. ¿Hay alguna manera de obtener lo que quiero, para obtener el n. ° 1 seleccionado al instalar "Foo "? como engañar al compilador para que piense que el # 1 es quizás más especializado. – keis

+0

+1, donde un +10 sería más apropiado. Me tomó tanto tiempo escribir la respuesta que me ganaste * solo * 29 minutos :) –

+2

@keis: El problema es que la especialización es un orden parcial, y en algunos casos, dos elementos realmente no están ordenados. Ahora, si declara lo que realmente está tratando de lograr (escriba una plantilla tal que cuando la instancia con int tendrá un valor de 1 mientras que una instancia con X tendrá un valor de 2), entonces puede obtener mejores pistas –

-1

no le faltaba un símbolo

< 

?

+0

< and > se perdió en el analizador HTML del sitio, espero haberlo solucionado ahora. – keis

-2

creo que le falta un '<', una plantilla debe verse como:

template< typename T > 
struct myStruct 
{}; 

//OR 

template< class T > 
struct myStruct 
{}; 
+0

Oh, arregló el código, esta respuesta es redundante ahora. – DeusAduro

+0

¿Por qué me rechazarías ...? Respondí antes de que se editara el código, en ese momento mi respuesta tenía sentido ... – DeusAduro

+1

Porque no tiene sentido * ahora *. Si no desea editar para responder la nueva pregunta, elimine la respuesta por completo. – avakar