Pregunta

La última pregunta que hice fue algo me topé cuando se trata de entender otra cosa ... que yo también no puedo entender (no es mi día).

Esta es una declaración bastante larga pregunta, pero al menos espero que esta pregunta podría ser de utilidad para muchas personas y no sólo a mí.

El código que tengo es la siguiente:

template <typename T> class V;
template <typename T> class S;

template <typename T>
class V
{
public:
 T x;

 explicit V(const T & _x)
 :x(_x){}

 V(const S<T> & s)
 :x(s.x){}
};

template <typename T>
class S
{
public:
 T &x;

 explicit S(V<T> & v)
 :x(v.x)
 {}
};

template <typename T>
V<T> operator+(const V<T> & a, const V<T> & b)
{
 return V<T>(a.x + b.x);
}

int main()
{
 V<float> a(1);
 V<float> b(2);
 S<float> c( b );

 b = a + V<float>(c); // 1 -- compiles
 b = a + c;           // 2 -- fails
 b = c;               // 3 -- compiles

 return 0;
}

Expresiones 1 y 3 de trabajo perfectamente, mientras que la expresión 2 no se compila.

Si he entendido bien, lo que ocurre es:

Expresión 1

  1. c es se convierte implícitamente a const mediante el uso de una secuencia de conversión estándar (que consiste en un solo conversión calificación ).
  2. V<float>(const S<T> & s) se llama y un objeto de la const V<float> temporal generada (llamémoslo t ). Ya es const cualificado, ya que es un valor temporal.
  3. a se convierte a const de manera similar a c .
  4. operator+(const V<float> & a, const V<float> & b) se llama, lo que resulta en un temporal del tipo const V<float> que podemos llamar q .
  5. el V<float>::operator=(const & V<float>) por defecto se llama.

¿Estoy bien hasta aquí? Si hice incluso el más sutil error por favor, hágamelo saber, porque yo estoy tratando de obtener una comprensión acerca de la conversión implícita lo más profundo posible ...

Expresión 3

  1. c se convierte en V<float>. Por eso, tenemos una secuencia de conversión definida por el usuario:
    1.1. conversión primer estándar:. S<float> a través de la conversión const S<float> cualificación
    1.2. Definida por el usuario de conversión: const S<float> a V<float> través V<float>(const S<T> & s) constructor
    . 1,3 segundos de conversión estándar:. V<float> a const V<float> través de la conversión de calificación
  2. el V<float>::operator=(const & V<float>) por defecto se llama.

Expresión 2?

Lo que no entiendo es por qué hay un problema con la segunda expresión. ¿Por qué es la siguiente secuencia no es posible?

  1. c se convierte en V<float>. Por eso, tenemos una secuencia de conversión definida por el usuario:
    1.1. estándar de conversión inicial:. S<float> a través de la conversión const S<float> cualificación
    1.2. Definida por el usuario de conversión: const S<float> a V<float> través V<float>(const S<T> & s) constructor
    . 1.3. conversión norma final: V<float> a const V<float> través de la conversión de calificación.
  2. Pasos 2 a 6 son los mismos que en caso de la expresión 1.

Después de leer el estándar de C ++ que sin embargo: '¡Hey! Tal vez el problema tiene que a la 13.3.3.1.2.3!' que establece:

  

Si la conversión definida por el usuario se especifica mediante una función de conversión de plantilla, la segunda secuencia de conversión estándar debe tener rango de coincidencia exacta.

Pero eso puede no ser el caso ya que la conversión tiene cualificación exacta rango partido ...

Realmente no tengo ni idea ...

Bueno, si usted tiene la respuesta o no, gracias por leer hasta aquí:)

¿Fue útil?

Solución

Como Edric- señaló, las conversiones no se consideran durante la deducción argumento de plantilla. Aquí, usted tiene dos contextos en los que la plantilla parámetro T puede deducirse a partir del tipo de los argumentos:

template<class T>
v<T> operator+(V<T> const&, V<T> const&);
               ~~~~~~~~~~~  ~~~~~~~~~~~~

Pero intenta invocar esta plantilla de función con un V<float> en el lado izquierdo y una S en el lado derecho. Plantilla resultados de deducción argumento en t = flotador para el lado izquierdo y obtendrá un error por el lado derecho, porque no hay manera que T es igual a V<T> S<T>. Esto califica como un fracaso deducción argumento de plantilla y la plantilla es simplemente ignorado.

Si desea permitir conversiones su operador + no debe ser una plantilla. No es el siguiente truco: Se puede definir como un amigo en línea dentro de la plantilla de clase para V:

template<class T>
class V
{
public:
   V();
   V(S<T> const&); // <-- note: no explicit keyword here

   friend V<T> operator+(V<T> const& lhs, V<T> const& rhs) {
      ...
   }
};

De esta manera, el operador no es una plantilla más. Por lo tanto, no hay necesidad de deducción argumento de plantilla y su invocación debería funcionar. El operador se encuentra a través ADL (lookup dependiente argumento) debido a que el lado izquierdo es un V<float>. El lado derecho se convierte adecuadamente a un V<float> también.

También es posible desactivar la deducción argumento de plantilla para un argumento específico. Por ejemplo:

template<class T>
struct id {typedef T type;};

template<class T>
T clip(
   typename id<T>::type min,
   T value,
   typename id<T>::type max )
{
   if (value<min) value=min;
   if (value>max) value=max;
   return value;
}

int main() {
   double x = 3.14;
   double y = clip(1,x,3); // works, T=double
}

A pesar de que el tipo del primer y último argumento es un entero, que no se consideran durante la plantilla de la deducción argumento porque id<T>::type no es una llamada * context` deducible. Por lo tanto, T solamente se deduce de acuerdo con el segundo argumento, que se traduce en T = doble sin contradicciones.

Otros consejos

Al considerar correspondencias del molde, no se utilizan las conversiones implícitas. Por lo tanto, en el siguiente ejemplo simple:

template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }

int main( int argc, char ** argv ) {
    foo( 1, 1.0 );
    return 0;
}

Eso no se compilará a pesar de que alguno de los argumentos se podría convertir implícitamente al otro tipo (int <-> doble).

Sólo una conjetura, pero tal vez el compilador no puede distinguir entre la conversión de V-> S o de S> V al tratar de encontrar la manera de poner a + c en la expresión 2. Usted está asumiendo el compilador será lo suficientemente inteligente como para escoger el que permite la recopilación de proceder, debido a que el resto de las funciones disponibles, pero el compilador es probable que no "leer por delante" (por así decirlo), y se está confundida con la ambigüedad de la conversión ascendente antes tratando de encontrar el operador '+'.

Por supuesto, si se ha añadido el error de compilación, que podría ayudar a aclarar el problema también ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top