问题描述
我正在为我的一个班级做模板专业化,但遇到了一些意想不到的事情..
I'm doing template specialization for one of my classes, and I'm experiencing something unexpected..
这是我的代码:
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) 可以接受任何类型的参数......除了类型基类.
I have a template function doSomething(T) that accepts parameters of any type.. except for type Base class.
所以我专门为 Base 类型的参数使用 doSomething 模板,所以它做了一些不同的事情.
So I specialized the doSomething template for parameter of type Base,, so it does something different.
然而,当我将派生类传递给 doSomething 时,它会打印所有类型!",而我预计它会打印基类!",因为派生类本质上也是基类.
When I pass a Derived class to doSomething, however, it prints "All Types!", while I expected it to print "Base!", because Derived class is essentially a Base class too.
为什么此专业化不适用于 Derived?
Why does this specialization not work for Derived?
有什么办法让它工作吗?
Any way to make it work?
谢谢
更新:
有人提到覆盖而不是模板专门化..但是在这种情况下我将如何覆盖函数?
Someone mentioned overriding instead of template specializing.. but how would I override function in this case?
如果我改为:
template<typename T>
void doSomething(T t) { cout << "All Types!" << endl; }
void doSomething(Base b) { cout << "Base!" << endl; }
然后 doSomething(d) 也会打印所有类型!"而不是Base!",因为 Derived2 对象将被简单地视为类型模板参数
then doSomething(d) would also print "All Types!" instead of "Base!",, because Derived2 object would simply be considered as Type template parameter
推荐答案
当你 doSomething(Derived)
时,你导致你的 template
被 T 推测性地实例化=派生
.
When you doSomething(Derived)
you cause your template
to be speculatively instantiated with T=Derived
.
这有效(没有 SFINAE),所以它成为一个候选.
This works (no SFINAE), so it becomes a candidate.
doSomething(Base)
要么不被考虑,要么是比 doSomething(Derived)
更差的匹配.
doSomething(Base)
is either not considered, or is a worse match than doSomething(Derived)
.
Specialization 只是改变了 doSomething
的一个实例化的实现.它根本不会改变它的考虑方式.
Specialization simply changes the implementation of that one instantiation of doSomething
. It does not change how it is considered at all.
覆盖添加另一个覆盖,然后使用通常的规则与您的 template
版本竞争.
Overriding adds another override, which then competes with your template
version using the usual rules.
有几种方法可以将调用路由到 doSomething
,这些调用通过 Base
或从 base 派生的任何类到单个实现.我会展示 2.
There are a few ways we can route calls to doSomething
that are passed a Base
or any class derived from base to a single implementation. I'll show 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
)
(replace {}
with ()
, and drop std::forward<T>
in C++03)
在这里,我们明确地将 Base
的派生类路由到不同的覆盖.
Here, we explicitly route derived classes of Base
to a different override.
另一种方法是 SFINAE 将 template
排除在考虑之外,并进行覆盖:
Another approach would be to SFINAE exclude the template
from consideration, and have an override:
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 排除在考虑之外,而是使用覆盖.
now the template
version is excluded from consideration by SFINAE, and the override is used instead.
我发现标签调度更干净.
I find tag dispatching to be more clean.
这篇关于模板专业化不适用于派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!