Ответ 1
Да, это возможно, но вам нужно немного изменить свой код.
Прежде всего, чтобы быть техническим, вторая функция f()
- это not специализация функции шаблона, но перегрузка. При разрешении перегрузки версия шаблона выбирается для всех аргументов, тип которых не является A
, потому что это идеальное совпадение: T
выводится равным типу аргумента, поэтому при вызове f(b)
для экземпляр, после вывода типа, компилятор должен будет выбрать между двумя следующими перегрузками:
void f(B&){printf("template\n");}
void f(A&){printf("specialization\n");}
Конечно, первое - лучшее совпадение.
Теперь, если вы хотите, чтобы вторая версия была выбрана при вызове функции с аргументом, который является подклассом A
, вы должны использовать некоторую технику SFINAE, чтобы предотвратить создание шаблона функции, когда тип T
выводится как подкласс A
.
Вы можете использовать std::enable_if
в сочетании с чертами типа std::is_base_of
, чтобы достичь этого.
// This will get instantiated only for those T which are not derived from A
template<typename T,
typename enable_if<
!is_base_of<A, T>::value
>::type* = nullptr
>
void f(T&) { cout << "template" << endl; }
Вот как вы могли бы использовать его в полной программе:
#include <type_traits>
#include <iostream>
using namespace std;
class A{};
class B : public A{};
class C : public B{};
class D {};
class Test
{
public:
template<typename T,
typename enable_if<!is_base_of<A, T>::value>::type* = nullptr
>
void f(T&) { cout << ("template\n"); }
void f(A&){ cout << ("non-template\n");}
};
int main()
{
A a;
B b;
C c;
D d;
float f;
Test test;
test.f(a); // Will print "non-template"
test.f(b); // Will print "non-template"
test.f(c); // Will print "non-template"
test.f(d); // Will print "template"
test.f(f); // Will print "template"
}
EDIT:
Если вы работаете с компилятором, который не полностью совместим с С++ 11 (и поэтому не поддерживает аргументы шаблона по умолчанию в шаблонах функций), вы можете изменить определение перегрузки шаблона f()
как следующим образом:
template<typename T>
typename enable_if<!is_base_of<A, T>::value, void>::type
f(T&) { cout << ("template\n"); }
Поведение программы будет идентичным. Обратите внимание: если тип возврата f()
равен void
, вы можете опустить второй аргумент в шаблон класса enable_if
.