2017-02-11 1 views
2

Используя приведенный ниже код, я получаю жалобу компилятора о том, что вызов get_code является двусмысленным между двумя первыми шаблонами. Как я могу обработать код для определения базового класса, а также предоставления специализированных форм? Например, если позже я получил class C : A {}, который также должен был вернуть ACLASS.Как я могу специализировать шаблон с использованием is_base_of без двусмысленности с основным шаблоном?

class A {}; 
class B : A {}; 
class D {}; 

enum Code { UNKNOWN, ACLASS, DCLASS }; 

template <typename T> 
Code get_code() { return Code::UNKNOWN; } 

template <typename T> 
typename std::enable_if<std::is_base_of<A, T>::value, Code>::type 
get_code() { return Code::ACLASS; } 

template <> 
inline Code get_code<D>() { return Code::DCLASS; } 

Code test1 = get_code<D>(); // OK, chooses DCLASS 
Code test2 = get_code<B>(); // ambiguous call to overloaded function 
+0

ли [это] (http://melpon.org/wandbox/permlink/N3MCPAclgAfI63J4), что вам ищем? – skypjack

+0

Да, это тоже похоже на хорошее решение! – Glenn

ответ

3

Вы должны деактивировать неизвестный случай, когда T является базой A

template <typename T> 
typename std::enable_if< ! std::is_base_of<A, T>::value, Code>::type 
get_code() // -----------^ 
{ return Code::UNKNOWN; } 

в противном случае, когда T является базой A, компилятор может использовать две версии get_code() и не может выбрать правый (Неоднозначный вызов)

Ниже приводится полный рабочий пример

#include <iostream> 
#include <type_traits> 

class A {}; 
class B : A {}; 
class C {}; 
class D {}; 

enum Code { UNKNOWN, ACLASS, DCLASS }; 

template <typename T> 
typename std::enable_if<!std::is_base_of<A, T>::value, Code>::type 
get_code() 
{ std::cout << "code U" << std::endl; return Code::UNKNOWN; } 

template <typename T> 
typename std::enable_if<std::is_base_of<A, T>::value, Code>::type 
get_code() 
{ std::cout << "code A" << std::endl; return Code::ACLASS; } 

template <> 
Code get_code<D>() 
{ std::cout << "code D" << std::endl; return Code::DCLASS; } 

int main() 
{ 
    get_code<A>(); // print A 
    get_code<B>(); // print A 
    get_code<C>(); // print U 
    get_code<D>(); // print D 
} 

Но я предлагаю вам еще один способ, основанный на тег диспетчеризации, чтобы получить тот же результат без использования SFINAE

#include <iostream> 
#include <type_traits> 

class A {}; 
class B : A {}; 
class C {}; 
class D {}; 

enum Code { UNKNOWN, ACLASS, DCLASS }; 

Code gc2h (std::true_type const &) 
{ std::cout << "code A" << std::endl; return Code::ACLASS; } 

Code gc2h (std::false_type const &) 
{ std::cout << "code U" << std::endl; return Code::UNKNOWN; } 

template <typename T> 
Code gc2() 
{ return gc2h(typename std::is_base_of<A, T>::type {}); } 

template <> 
Code gc2<D>() 
{ std::cout << "code D" << std::endl; return Code::DCLASS; } 

int main() 
{ 
    gc2<A>(); // print A 
    gc2<B>(); // print A 
    gc2<C>(); // print U 
    gc2<D>(); // print D 
} 

Другим способом может быть передать значение std::is_base_of в качестве параметра шаблона для вспомогательной функции

#include <iostream> 
#include <type_traits> 

class A {}; 
class B : A {}; 
class C {}; 
class D {}; 

enum Code { UNKNOWN, ACLASS, DCLASS }; 

template <bool> 
Code gc3h(); 

template <> 
Code gc3h<true>() 
{ std::cout << "code A" << std::endl; return Code::ACLASS; } 

template <> 
Code gc3h<false>() 
{ std::cout << "code U" << std::endl; return Code::UNKNOWN; } 

template <typename T> 
Code gc3() 
{ return gc3h<std::is_base_of<A, T>::value>(); } 

template <> 
Code gc3<D>() 
{ std::cout << "code D" << std::endl; return Code::DCLASS; } 

int main() 
{ 
    gc3<A>(); // print A 
    gc3<B>(); // print A 
    gc3<C>(); // print U 
    gc3<D>(); // print D 
} 

- EDIT -

Другим возможным решением.

Если вы можете принять, что функция является static метод шаблона class (или struct), и если вы можете принять, что называется gc4<T>::func() вместо gc4<T>(), другой способ, основанный на частичной специализации, следующим образом.

#include <iostream> 
#include <type_traits> 

class A {}; 
class B : A {}; 
class C {}; 
class D {}; 

enum Code { UNKNOWN, ACLASS, DCLASS }; 

template <typename T, bool = std::is_base_of<A, T>::value> 
struct gc4; 

template <typename T> 
struct gc4<T, true> 
{ 
    static_assert(true == std::is_base_of<A, T>::value, "!"); 

    static Code func() 
    { std::cout << "code A" << std::endl; return Code::ACLASS; } 
}; 

template <typename T> 
struct gc4<T, false> 
{ 
    static_assert(false == std::is_base_of<A, T>::value, "!!"); 

    static Code func() 
    { std::cout << "code U" << std::endl; return Code::UNKNOWN; } 
}; 

template <> 
struct gc4<D> 
{ 
    static Code func() 
    { std::cout << "code D" << std::endl; return Code::DCLASS; } 
}; 

int main() 
{ 
    gc4<A>::func(); // print A 
    gc4<B>::func(); // print A 
    gc4<C>::func(); // print U 
    gc4<D>::func(); // print D 
} 

The static_assert() с добавляются, чтобы избежать, что кто-то может обойти решение, призывающее что-то вроде

gc4<A, false>::func(); 
+0

Отличный ответ! Я ценю наличие подробных примеров кода, а также множество рабочих параметров. – Glenn

+0

@Glenn - посмотрите также на ссылку, предложенную Skypjack: покажите другой тип решения, основанный на двух функциях, где включен, через 'std :: enable_if', является фаворитом (если доступно) над другим; поэтому нет необходимости удалять вторую функцию при первом включении. – max66

+0

@Glenn - Потому что вы цените множество рабочих параметров, я добавил еще один. – max66

Смежные вопросы