为什么默认模板函数被选为比其专用版本更匹配?



我有以下模板成员函数:

template <class ParameterT>
typename boost::enable_if_c<boost::is_base_of<BaseOne, ParameterT>::value
|| boost::is_base_of<BaseTwo, ParameterT>::value, void>::type
MyClass::doSomething(const boost::shared_ptr<ParameterT> &param);

调用doSomething(sharedPtrTo_derivedFromBaseOne)doSomething(sharedPtrTo_derivedFromBaseTwo)doSomething(sharedPtrTo_derivedFromBaseOneAndBaseTwo)都可以,使用任何其他参数调用它都不起作用,这确实是我到目前为止想要的。

现在在上述实现中,我还需要两个调用:doSomething_baseOnedoSomething_baseTwo。显然,它们应该针对到目前为止进行的所有调用进行编译(因此我的shared_ptr参数要么是派生BaseOne,要么是派生BaseTwo,或两者兼而有之)。我想到了这样的事情:

template<class BaseOneDerived>
void doSomething_baseOne(const boost::shared_ptr<BaseOneDerived> &param);
{
std::cout << "doing nothing BaseOne";
}
template<class BaseTwoDerived>
void doSomething_baseTwo(const boost::shared_ptr<BaseTwoDerived> &param)
{
std::cout << "doing nothing BaseTwo";
}
//doSomething implementation:
{
doSomething_baseOne(param);
doSomething_baseTwo(param);
}

doSomething_baseOnedoSomething_baseTwo专业:

template<>
void MyClass::doSomething_baseOne(const boost::shared_ptr<BaseOne> &param)
{
std::cout << "doing something with BaseOne";
}
template<>
void MyClass::doSomething_baseTwo(const boost::shared_ptr<BaseTwo> &param)
{
std::cout << "doing something with BaseTwo";
}

现在假设我有这个简单的层次结构:

class A : public BaseOne {};

class B : public BaseTwo {};

class C : public BaseOne, public BaseTwo {};

我想,对于以下电话:

MyClass X;
X.doSomething(boost::shared_ptr(new A());
X.doSomething(boost::shared_ptr(new B());
X.doSomething(boost::shared_ptr(new C());

要接收以下输出,请执行以下操作:

//for first call
doing something with BaseOne
doing nothing BaseTwo
//for second call
doing nothing BaseOne
doing something with BaseTwo
//for third call
doing something with BaseOne
doing something with BaseTwo

相反,我收到 6 次"什么都不做"消息(实际上我没有实现非专用版本,所以我实际上只是收到一个链接时间未定义的引用,但你明白了)。

因此,基本上编译器会选择默认doSomething_baseOne,并doSomething_baseTwo比专用版本更好的匹配。为什么?我怎样才能克服这一点来实现我想要的?

这是因为编译器实例化以下模板:

template<>
void doSomething_baseOne(const boost::shared_ptr<A> &param)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<A> &param)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseOne(const boost::shared_ptr<B> &param)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<B> &param)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseOne(const boost::shared_ptr<C> &param)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<C> &param)
{
std::cout << "doing nothing BaseOne";
}

哪些模板专业化比您提供的模板更好。

这样做,使用enable_if技巧:

template<class T>
std::enable_if_t<std::is_base_of<T,BaseOne>::value>
doSomething_baseOne(const boost::shared_ptr<T>& param){
std::cout << "doing something baseOne";
}
template<class T>
std::enable_if_t<!std::is_base_of<T,BaseOne>::value>
doSomething_baseOne(const boost::shared_ptr<T>& param){
std::cout << "doing nothing baseOne";
}
template<class T>
std::enable_if_t<std::is_base_of<T,BaseTwo>::value>
doSomething_baseTwo(const boost::shared_ptr<T>& param){
std::cout << "doing something baseTwo";
}
template<class T>
std::enable_if_t<!std::is_base_of<T,BaseTwo>::value>
doSomething_baseTwo(const boost::shared_ptr<T>& param){
std::cout << "doing nothing baseTwo";
}

或者这样,使用标记调度:

template<class T>
doSomething_baseOne(const boost::shared_ptr<T>& param
,std::integral_constant<bool,true>){
std::cout << "doing something baseOne";
}
template<class T>
doSomething_baseOne(const boost::shared_ptr<T>& param
,std::integral_constant<bool,false>){
std::cout << "doing nothing baseOne";
}
template<class T>
doSomething_baseOne(const boost::shared_ptr<T>& param){
doSmething_baseOne(param,
std::integral_constant<
bool,std::is_base_of<T,BaseOne>::value
>{});
}
template<class T>
doSomething_baseTwo(const boost::shared_ptr<T>& param
,std::integral_constant<bool,true>){
std::cout << "doing something baseTwo";
}
template<class T>
doSomething_baseTwo(const boost::shared_ptr<T>& param
,std::integral_constant<bool,false>){
std::cout << "doing nothing baseTwo";
}
template<class T>
doSomething_baseTwo(const boost::shared_ptr<T>& param){
doSmething_baseTwo(param,
std::integral_constant<
bool,std::is_base_of<T,BaseTwo>::value
>{});
}

更好的解决方案是使用概念。甚至更干净,如果(C++17)。

最新更新