5

Przy następujących szablony funkcyjne:Dlaczego kompilator nie wybiera przeciążenia szablonu funkcji w poniższym przykładzie?

#include <vector> 
#include <utility> 

struct Base { }; 
struct Derived : Base { }; 

// #1 
template <typename T1, typename T2> 
void f(const T1& a, const T2& b) 
{ 
}; 

// #2 
template <typename T1, typename T2> 
void f(const std::vector<std::pair<T1, T2> >& v, Base* p) 
{ 
}; 

Dlaczego jest to, że następujący kod zawsze wywołuje przeciążenia nr 1 zamiast przeciążeniem # 2?

int main() 
{ 
    std::vector<std::pair<int, int> > v; 
    Derived derived; 

    f(100, 200); // clearly calls overload #1 
    f(v, &derived);   // always calls overload #1 

    return 0; 
} 

Biorąc pod uwagę, że drugi parametr f to rodzaj pochodzi z Base, miałem nadzieję, że kompilator wybrałby przeciążeniem nr 2, ponieważ jest to lepszy mecz niż typu rodzajowego przeciążenie # 1.

Czy są jakieś techniki, których można użyć do przepisania tych funkcji, aby użytkownik mógł napisać kod wyświetlany w funkcji main (tj. Wykorzystując dedukcję argumentów typu kompilator)?

+0

Chociaż nie ma to związku z głównym pytaniem, nadal: 1. "int main()", proszę. 2. Po definicjach funkcji szablonu pojawiają się puste deklaracje ";". Jest to nielegalne w C++. – AnT

Odpowiedz

12

Można to zrobić:

f(v, static_cast<Base*>(&derived)); 

Albo użyć SFINAE usunąć pierwszą funkcja jako kandydat do selekcji:

// Install boost library and add these headers: 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits.hpp> 

// #1 - change it to look like this (note the keyword void changed positions) 
template <typename T1, typename T2> 
typename boost::disable_if< 
    typename boost::is_convertible<T2, Base*>, void>::type 
f(const T1& a, const T2& b) 
{ 
}; 

// #2 - this one can stay the same 
template <typename T1, typename T2> 
void f(const std::vector<std::pair<T1, T2> >& v, Base* p) 
{ 
}; 
+0

Zwiększaj na ratunek! Bardzo mądry! –

+0

Dang Właśnie dodałem to. :) – GManNickG

+0

Jedyna smutna uwaga jest taka, że ​​jest o wiele mniej czytelna ...+1 w każdym razie, wyjaśnienie jest dobre, proponowanie rozwiązania jest jeszcze lepsze :) –

8

Biorąc pod uwagę, że drugi parametr f jest typu pochodzą z bazy

To zamienny do takich, ale jest to pochodna *. Pierwsza funkcja szablonu nie wymaga konwersji, a druga wymaga jednej, dlatego wybiera pierwszą.

ten wybiera drugi:

f(v, static_cast<Base*>(&derived)); 

Na marginesie, main zwraca int.

+0

Dziękuję za wyjaśnienie. Usunąłem wartość zwracaną z głównej, ponieważ nie było to konieczne w przykładzie. –

+0

Cóż, główny ma domyślny "return 0;", więc możesz zostawić int anyway :) I to jest o jeden znak krócej! : P – GManNickG

+3

Konieczny lub nie, język C++ _wymaga_, że "główny" jest zadeklarowany z typem zwracanym "int". – AnT

1

Oprócz oczywistych tematów dotyczących Koenig Lookup, które są mniej lub bardziej dobrze zaimplementowane przez kompilatory (szczególnie starsze są dość problematyczne), istnieje kilka pułapek dotyczących template specialization.

Specjalizacja wymaga dokładnego dopasowania typów (nie wiem, jak definiuje to std, ale z mojego doświadczenia [gcc, msvc] klasa pochodna nie zostanie dopasowana). Jeśli dodać brzydki odcień podstawowy *, to powinno działać jak zamierzają ewentualnie dodać kolejną specjalizację dla Pochodzące ...

Powiązane problemy