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

时间:2017-02-20 10:35:49

标签: c++ templates c++03

我有以下模板成员函数:

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

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

所以基本上编译器选择默认的doSomething_baseOnedoSomething_baseTwo作为比专用版本更好的匹配。这是为什么?我怎样才能克服这一点来实现我想要的目标呢?

1 个答案:

答案 0 :(得分:3)

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

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
                               >{});
}

更好的解决方案是使用概念。甚至更清洁,constexpr if(C ++ 17)。