2011-11-17 22 views
15

int x = fromString("test"): no podía deducirse argumento de plantilla para 'ValueType'¿Por qué C++ no puede deducir el tipo de plantilla de la asignación?

int x = fromString<int>("test"): funciona bien como se esperaba

Entonces, ¿por qué la lucha compilador de aquí? Lo veo con todo tipo de funciones de plantillas reales, no solo con este ejemplo tonto. Debe ser una característica del lenguaje, pero ¿qué?

+10

¿Por qué es algo que se puede convertir en un entero llamado "toString"? –

+2

[Esta pregunta] (http: // stackoverflow.com/questions/442026/function-overloading-by-return-type) discute un problema similar. –

+0

"Simplemente no". Tenga en cuenta que este es el lenguaje, no el compilador, aunque en realidad no está especificado en el lenguaje en parte porque sería complicado para el compilador en el caso general. –

Respuesta

12

C++ no hace inferencia de tipo sobre el valor de retorno. Es decir, el hecho de que se está asignando a un int no se usa en la deducción del parámetro de la plantilla.

(edición eliminado, ya que otra persona presentó la solución fundido sobrecargado ya.)

+1

Gracias. No lo necesito, es solo una de esas peculiaridades ligeramente irritantes y quería entender por qué. –

1

Parece que la plantilla tiene el tipo de retorno con plantilla que no se pueden deducirse automáticamente por lo que es necesario agregar que aquí.

+0

La declaración del tipo de devolución diferida (o como se llame) tiene un propósito diferente. Le permite declarar el tipo de devolución según el tipo de argumentos de la función. Simplemente pospone declarando el tipo de devolución hasta que los argumentos estén dentro del alcance. No permite deducir el tipo de resultado del uso de la persona que llama. – visitor

+0

Gracias por el comentario, eliminaré de mi publicación :-) – Firedragon

0

Además de la mala elección para un ejemplo (probablemente tiene sentido tener int x = to<int>("1235") en lugar de toString), el problema es que el tipo de retorno no participa en la resolución de sobrecarga o la inferencia de tipos [1]. La razón de esto es que la expresión se puede utilizar en muchos lugares en los que el tipo de la devolución no se puede deducir:

// assuming template <typename T> T to(std::string): 
// 
f(to("123"));   // where there are two overloads f(int), f(double) 
int x = 1.5 * to("123"); // T == int? T == double? 
to("123");    // now what? returned object can be ignored! 

Así que la decisión es que el tipo de retorno no tomará parte en la resolución de sobrecarga o el tipo de deducción.

[1] Hay una sola excepción a esta regla, que es la evaluación de un puntero de función con más de una sobrecarga, en el que la sobrecarga se debe seleccionar ya sea por el puntero de destino o una conversión explícita, pero esto es solo la única excepción y no se usa en ningún otro contexto:

void f(); 
void f(int); 
void g(void (*)()); 
void g(void (*)(int)); 

void (*p1)() = &f;  // overload selected based on destination type 
void (*p2)(int) = &f; 
g((void (*)(int))&f); // overload selected based on explicit cast 
+0

"un puntero a la función", incluida la función puntero-a-miembro. –

16

No se puede deducir basándose en el tipo de devolución. Puede, sin embargo, poner en práctica una solución con una sintaxis similar, utilizando el operador de conversión sobrecargado:

#include <iostream> 
#include <sstream> 
#include <string> 
using namespace std; 

class FromString{ 
private: 
    string m_data; 
public: 
    FromString(const char*data) : m_data(data) {} 

    template<typename T> 
    operator T(){ 
     T t; 
     stringstream ss(m_data); 
     ss >> t; 
     return t; 
    } 

}; 

template<> FromString::operator bool(){ 
    return (m_data!="false"); //stupid example 
} 

int main(){ 

    int ans = FromString("42");  
    bool t = FromString("true"); 
    bool f = FromString("false"); 

    cout << ans << " " << t << " " << f << endl; 

    return 0; 
} 

Salida:

42 1 0 
+0

Función de conversión universal, ¡ay! Pero como 'operator =' no puede ser un "amigo", supongo que esta es la mejor solución, +1. – Potatoswatter

0

El tipo de retorno de una función depende de la resolución de sobrecarga, no a la inversa alrededor.

Hay un truco que funciona sin embargo: operator= por lo general sólo existe por la igualdad de LHS/RHS tipos de argumentos, excepto cuando se define una explícita operator= (ya sea como autónomo o como un miembro no importa).

Por lo tanto, la resolución de sobrecarga encontrará operator=(int &, int), y verá si el valor de retorno de su función es convertible a int. Si devuelve un temporal que tiene un operator int, esta es una resolución aceptable (incluso si el operator int tiene el formato genérico de template<typename T> operator T).

Por lo tanto:

template<typename T, typename U> 
U convert_impl(T const &t); 

template<typename T> 
struct convert_result { 
    convert_result(T const &t) : t(t) { } 
    template<typename U> operator U(void) const { return convert_impl<U>(t); } 
    T const &t; 
}; 

template<typename T> 
convert_result<T> convert(T const &t) { return t; } 
Cuestiones relacionadas