5

Учитывая следующие шаблоны функций:Почему компилятор не выбирает мою функцию-перегрузку шаблона в следующем примере?

#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) 
{ 
}; 

Почему следующий код всегда вызывает перегрузку # 1 вместо перегрузки # 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; 
} 

Учитывая, что второй параметр f является производным типом Base, я надеялся, что компилятор будет выбирать перегрузки # 2, как это лучше, чем матч общего типа в перегрузки # 1.

Есть ли какие-либо методы, которые я мог бы использовать для перезаписи этих функций, чтобы пользователь мог писать код, отображаемый в функции main (т. Е. Используя компилятор-вывод типов аргументов)?

+0

Хотя это не связано с основным вопросом, все еще: 1. «int main()», пожалуйста. 2. За определением функции шаблона следуют пустые объявления ';'. Это незаконно в C++. – AnT

ответ

12

Вы можете сделать это:

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

Или использовать SFINAE, чтобы удалить первый в качестве кандидата выбора:

// 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

Подъем на помощь! Очень умно! –

+0

Dang Я просто добавлял это. :) – GManNickG

+0

Единственная печальная нота в том, что она настолько менее читаема ...+1 в любом случае, объяснение хорошее, предлагая решение еще лучше :) –

8

Учитывая то, что второй параметр F является производным тип базы

Это конвертируемые в такие, но это является производным *. Первая функция шаблона не требует преобразований, а вторая требует одного, поэтому выбирает первый.

Это выбирает второе:

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

На стороне записки, main возвращает int.

+0

Спасибо за объяснение. Я удалил возвращаемое значение из main, поскольку в этом примере это не было необходимо. –

+0

Ну, у main есть неявный 'return 0;', поэтому вы можете оставить int anyway :) И это один символ короче! : P – GManNickG

+3

Нужно или нет, язык C++ _requires_, что 'main' объявлен с типом возврата 'int'. – AnT

1

Рядом с очевидными темами Koenig Lookup, которые более или менее хорошо реализованы компиляторами (особенно старые, довольно проблематичны), есть несколько подводных камней относительно template specialization.

Специализация требует, чтобы типы точно соответствовали (не уверены, как std определяет это, но из моего опыта [gcc, msvc] производный класс не будет сопоставлен). Если добавить некрасивый оттенок Base *, он должен работать, как вы собираетесь, возможно добавить еще одну специализацию Derived ...

Смежные вопросы