10

usando VC++ 2010, teniendo en cuenta lo siguiente:¿Por qué el compilador elige esta plantilla sobre una función no plantilla sobrecargada?

class Base { }; 
class Derived : public Base { }; 

template<class T> void foo(T& t); // A 
void foo(Base& base);    // B 

Derived d; 
foo(d);       // calls A 
foo(static_cast<Base&>(d));  // calls B 

me gustaría "B" para ser llamado anteriormente. Puedo lograr esto con un molde a Base, pero ¿por qué es esto necesario?

Quiero la función de plantilla que se llama para todos los tipos no derivados de Base (tipos incorporados, etc.), pero quiero que la sobrecarga no molde a ser llamado para este tipo de derivados de Base, sin que se requiera el cliente para emitir explícitamente. También intenté convertir la sobrecarga en una especialización de la plantilla, pero el mismo comportamiento ocurre en ese caso. ¿Cuál es la forma idiomática de obtener lo que estoy buscando?

Respuesta

12

En igualdad de condiciones, las funciones de no plantilla son preferibles a las plantillas de funciones. Sin embargo, en su escenario, todas las cosas no son iguales: (A) es una coincidencia exacta con T = Derived, pero (B) requiere una conversión derivada a base del argumento.

Puede solucionar esto para casos específicos (como éste) mediante el uso de SFINAE (fallo de sustitución no es un error) para evitar que (A) de ser instanciado con un tipo que se deriva de Base:

#include <type_traits> 
#include <utility> 

template <typename T> 
typename std::enable_if< 
    !std::is_base_of<Base, T>::value 
>::type foo(T& x) 
{ 
} 

void foo(Base& x) 
{ 
} 
+0

¿Esto es algo de C++ 11 o también funciona en código anterior? Solo pregunta porque es genial :)? –

+1

@ w00te: 'is_base_of' y' enable_if' están incluidos en Boost, C++ TR1 y C++ 11. No se requiere funcionalidad C++ 11 para ellos; puede definir estos utilizando solo características de lenguaje C++ 03. –

+0

@ w00te: puedes hacerlo en C++ 03, pero tienes que tomar 'enable_if' y' is_base_of' de boost (o lanzar el tuyo, esto te enseñará algunas cosas sobre C++). –

2

Se está seleccionando la versión de la plantilla porque es una coincidencia mejor cuando se la llama con un argumento del tipo Derived que la versión sobrecargada. Puede usar SFINAE para eliminar la versión de la plantilla de la resolución de sobrecarga, de modo que la otra versión se seleccione al llamar con argumentos del tipo Base o Derived.

#include <type_traits> 
#include <iostream> 

class Base { }; 
class Derived : public Base { }; 

template<class T> 
typename std::enable_if< 
    std::is_base_of<Base, T>::value == false 
>::type 
foo(T&) 
{ 
    std::cout << "template foo" << std::endl; 
} 


void foo(Base&) 
{ 
    std::cout << "non-template foo" << std::endl; 
} 


int main() 
{ 
    Derived d; 
    Base b; 

    foo(d); 
    foo(b); 
} 
+0

'is_same' es innecesario aquí. – Pubby

+0

@Pubby Gracias, lo arreglaré – Praetorian

Cuestiones relacionadas