10

Utilizzando VC++ 2010, data la seguente:Perché il compilatore sceglie questa funzione modello su una funzione non modello sovraccaricata?

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 

vorrei "B" per essere chiamato in precedenza. Posso ottenere questo risultato con un cast a Base, ma perché è necessario?

Desidero richiamare la funzione template per tutti i tipi non derivati ​​da Base (tipi incorporati, ecc.), Ma desidero che il sovraccarico non di modello venga chiamato per i tipi derivati ​​da Base, senza richiedere il client cast esplicito. Ho anche provato a rendere il sovraccarico una specializzazione del modello, ma lo stesso comportamento si verifica in quel caso. Qual è il modo idiomatico per ottenere ciò che sto cercando?

risposta

12

A parità di condizioni, le funzioni nontemplate sono preferite rispetto ai modelli di funzione. Tuttavia, nel tuo scenario, tutte le cose non sono uguali: (A) è una corrispondenza esatta con T = Derived, ma (B) richiede una conversione dell'argomento basata su una base.

È possibile risolvere questo casi specifici (come questo) usando SFINAE (mancata sostituzione non è un errore) per prevenire (A) venga istanziata con un tipo derivato da 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

Si tratta di una cosa C++ 11 o funziona anche nel vecchio codice - basta chiedere perché è bello :)? –

+1

@ w00te: 'is_base_of' e' enable_if' sono inclusi in Boost, C++ TR1 e C++ 11. Per loro non è richiesta alcuna funzionalità C++ 11; puoi definirli usando solo le caratteristiche del linguaggio C++ 03. –

+0

@ w00te: puoi farlo in C++ 03, ma devi prendere 'enable_if' e' is_base_of' da boost (o lanciare il tuo, questo ti insegnerà alcune cose su C++). –

2

La versione del modello viene selezionata perché è una corrispondenza migliore quando viene chiamata con un argomento di tipo Derived rispetto alla versione sovraccaricata. È possibile utilizzare SFINAE per eliminare la versione del modello dalla risoluzione di sovraccarico in modo che l'altra versione sia selezionata quando si chiama con argomenti di 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' non è necessario qui. – Pubby

+0

@Pubby Grazie, lo aggiusterò – Praetorian