我正在为我的一个班级做模板专业化,而且我遇到了意想不到的事情......
这是我的代码:
class Base {};
class Derived : public Base {};
template<typename T>
void doSomething(T t) { cout << "All Types!" << endl; }
template<>
void doSomething(Base b) { cout << "Base!" << endl; }
int main() {
Derived d;
doSomething(d); //prints "All Types!"
return 0;
}
我有一个模板函数doSomething(T),它接受任何类型的参数..除了类型Base类。
所以我将doSomething模板专门用于Base类型的参数,所以它做了不同的事情。
当我将Derived类传递给doSomething时,它会打印“All Types!”,而我期望它打印“Base!”,因为Derived类本质上也是一个Base类。
为什么此专业化不适用于Derived?
任何使其有效的方法?
由于
的更新:
有人提到覆盖而不是模板专门化..但在这种情况下如何覆盖函数?
如果我改为:
template<typename T>
void doSomething(T t) { cout << "All Types!" << endl; }
void doSomething(Base b) { cout << "Base!" << endl; }
然后doSomething(d)也会打印“All Types!”而不是“Base!”,因为Derived2对象将被简单地视为类型模板参数
答案 0 :(得分:3)
您可以使用std :: enable_if:
解决它#include <type_traits>
#include <iostream>
class Base {};
class Derived1 : public Base {};
class Derived2 : public Derived1 {};
template<typename T>
typename std::enable_if< ! std::is_base_of<Base, T>::value>::type
doSomething(const T&) { std::cout << "All Types!" << std::endl; }
template<typename T>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
doSomething(const T&) { std::cout << "Base or Derived!" << std::endl; }
int main() {
// All Types!
doSomething(1);
// Base or Derived!
doSomething(Base());
doSomething(Derived1());
doSomething(Derived2());
return 0;
}
注意:参数是const T&
,但是以T为参数可以避免上面提到的切片。
注意:typename std::enable_if<std::is_base_of<Base, T>::value>::type
是返回类型,它是std :: enable_if的可选第二个模板参数,默认为void。
答案 1 :(得分:3)
当doSomething(Derived)
{...}}时,template
会使T=Derived
被推测性地实例化。
这有效(没有SFINAE),因此它成为候选人。
doSomething(Base)
要么不被考虑,要么比doSomething(Derived)
更糟糕。
专业化只是改变doSomething
的一个实例化的实现。它根本不会改变它的考虑方式。
覆盖会添加另一个覆盖,然后使用通常的规则与您的template
版本竞争。
我们可以通过几种方式将doSomething
的调用路由到Base
或从base派生的任何类传递给单个实现。我将展示2。
首先,标记调度。
namespace aux {
template<class T> void doSomething( std::true_type /* is Base */, T t ) {
// T is a class derived from Base
}
template<class T> void doSomething( std::false_type /* is Base */, T t ) {
// T is not class derived from Base
}
}
template<class T> void doSomething( T t ) {
aux::doSomething( std::is_base_of< Base, T >{}, std::forward<T>(t) );
}
(将{}
替换为()
,然后在C ++ 03中删除std::forward<T>
在这里,我们明确地将Base
的派生类路由到不同的覆盖。
另一种方法是SFINAE不考虑template
,并覆盖:
template<class T>
typename std::enable_if< !std::is_base_of<Base, T>::value >::type
doSomething( T t ) { /* blah */ }
void doSomething( Base b ) { /* foo */ }
现在template
版本被SFINAE排除在考虑之外,而是使用覆盖。
我发现标签调度更加干净。