2017-02-20 12 views
3

У меня есть следующие функции-члена шаблона:Почему функция шаблона по умолчанию выбрана лучше, чем ее специализированная версия?

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_baseOne и doSomething_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_baseOne, doSomething_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 в качестве лучших совпадений, чем в специализированных версиях. Почему это? И как я могу преодолеть это, чтобы достичь того, чего хочу?

ответ

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).

+0

К сожалению, нет никаких концепций в C++ 03 AFAIK. Я думал, что мне нужно еще немного SFINAE, но на самом деле не было уверен, как это сделать. Таким образом, в основном SFINAE - единственное решение, когда вам нужно просить компилятор выбрать шаблон по умолчанию в качестве «последнего средства»? –

+0

Это другое решение, называемое * отправкой тега *. Это когда-нибудь более читаемо, чем использование триггера enable_if SFINAE. – Oliv

+0

@TedyS. ваш вопрос слишком широк, есть много факторов, которые могут повлиять на разрешение перегрузки, например, вы можете использовать [только один enable_if] (http://melpon.org/wandbox/permlink/iX2ePOel7KCcg4nv), или вы можете использовать [tag -dispatching] (http://melpon.org/wandbox/permlink/huRJhu6wzbJGwEJg) –