Почему компилятор выбирает эту функцию шаблона вместо перегруженной не шаблонной функции? - PullRequest
10 голосов
/ 21 декабря 2011

Используя VC ++ 2010, учитывая следующее:

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

Я бы хотел, чтобы "B" вызывался выше.Я могу добиться этого с приведением к Base, но зачем это нужно?

Я хочу, чтобы функция шаблона вызывалась для всех типов, не производных от Base (встроенные типы и т. Д.), но я хочу, чтобы не шаблонная перегрузка вызывалась для типов, производных от Base, без необходимости явного приведения клиента.Я также попытался сделать перегрузку специализацией шаблона, но в этом случае происходит то же самое.Какой идиоматический способ получить то, что я ищу?

Ответы [ 2 ]

12 голосов
/ 21 декабря 2011

При прочих равных, не шаблонные функции предпочтительнее шаблонов функций. Однако в вашем сценарии все вещи не равны: (A) является точным соответствием с T = Derived, но (B) требует преобразования аргумента в производную-основную базу.

В некоторых случаях (например, в этом) вы можете обойти эту проблему, используя SFINAE (ошибка замены не является ошибкой), чтобы предотвратить создание экземпляра (A) с типом, производным от 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)
{
}
2 голосов
/ 21 декабря 2011

Выбирается версия шаблона, потому что она лучше подходит при вызове с аргументом типа Derived, чем перегруженная версия.Вы можете использовать SFINAE для удаления версии шаблона из разрешения перегрузки, чтобы при вызове с аргументами типа Base или 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 );
}
была выбрана другая версия.
...