为什么选择默认模板函数比其专用版本更匹配?
Why is a default template function picked as a better match than its specialized version?
我有以下模板成员函数:
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> ¶m);
调用doSomething(sharedPtrTo_derivedFromBaseOne)
、doSomething(sharedPtrTo_derivedFromBaseTwo)
或doSomething(sharedPtrTo_derivedFromBaseOneAndBaseTwo)
都可以,用任何其他参数调用都不行,这确实是我目前想要的。
现在在上面的实现中,我还想要两个调用:doSomething_baseOne
和 doSomething_baseTwo
。显然,它们应该针对到目前为止进行的所有调用进行编译(因此我的 shared_ptr
参数是 BaseOne
派生或 BaseTwo
派生,或两者兼而有之)。我想到了这样的事情:
template<class BaseOneDerived>
void doSomething_baseOne(const boost::shared_ptr<BaseOneDerived> ¶m);
{
std::cout << "doing nothing BaseOne";
}
template<class BaseTwoDerived>
void doSomething_baseTwo(const boost::shared_ptr<BaseTwoDerived> ¶m)
{
std::cout << "doing nothing BaseTwo";
}
//doSomething implementation:
{
doSomething_baseOne(param);
doSomething_baseTwo(param);
}
以及 doSomething_baseOne
、doSomething_baseTwo
专长:
template<>
void MyClass::doSomething_baseOne(const boost::shared_ptr<BaseOne> ¶m)
{
std::cout << "doing something with BaseOne";
}
template<>
void MyClass::doSomething_baseTwo(const boost::shared_ptr<BaseTwo> ¶m)
{
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 次 "doing nothing with" 消息(实际上我还没有实现非专用版本,所以我真的只是收到了 link 次未定义的引用,但是你明白了)。
所以基本上编译器会选择默认的 doSomething_baseOne
和 doSomething_baseTwo
作为比专用版本更好的匹配。这是为什么?我怎样才能克服这个问题来实现我想要的?
这是因为编译器实例化了以下模板:
template<>
void doSomething_baseOne(const boost::shared_ptr<A> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<A> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseOne(const boost::shared_ptr<B> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<B> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseOne(const boost::shared_ptr<C> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<C> ¶m)
{
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).
我有以下模板成员函数:
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> ¶m);
调用doSomething(sharedPtrTo_derivedFromBaseOne)
、doSomething(sharedPtrTo_derivedFromBaseTwo)
或doSomething(sharedPtrTo_derivedFromBaseOneAndBaseTwo)
都可以,用任何其他参数调用都不行,这确实是我目前想要的。
现在在上面的实现中,我还想要两个调用:doSomething_baseOne
和 doSomething_baseTwo
。显然,它们应该针对到目前为止进行的所有调用进行编译(因此我的 shared_ptr
参数是 BaseOne
派生或 BaseTwo
派生,或两者兼而有之)。我想到了这样的事情:
template<class BaseOneDerived>
void doSomething_baseOne(const boost::shared_ptr<BaseOneDerived> ¶m);
{
std::cout << "doing nothing BaseOne";
}
template<class BaseTwoDerived>
void doSomething_baseTwo(const boost::shared_ptr<BaseTwoDerived> ¶m)
{
std::cout << "doing nothing BaseTwo";
}
//doSomething implementation:
{
doSomething_baseOne(param);
doSomething_baseTwo(param);
}
以及 doSomething_baseOne
、doSomething_baseTwo
专长:
template<>
void MyClass::doSomething_baseOne(const boost::shared_ptr<BaseOne> ¶m)
{
std::cout << "doing something with BaseOne";
}
template<>
void MyClass::doSomething_baseTwo(const boost::shared_ptr<BaseTwo> ¶m)
{
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 次 "doing nothing with" 消息(实际上我还没有实现非专用版本,所以我真的只是收到了 link 次未定义的引用,但是你明白了)。
所以基本上编译器会选择默认的 doSomething_baseOne
和 doSomething_baseTwo
作为比专用版本更好的匹配。这是为什么?我怎样才能克服这个问题来实现我想要的?
这是因为编译器实例化了以下模板:
template<>
void doSomething_baseOne(const boost::shared_ptr<A> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<A> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseOne(const boost::shared_ptr<B> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<B> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseOne(const boost::shared_ptr<C> ¶m)
{
std::cout << "doing nothing BaseOne";
}
template<>
void doSomething_baseTwo(const boost::shared_ptr<C> ¶m)
{
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).