Вы должны деактивировать неизвестный случай, когда 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();
ли [это] (http://melpon.org/wandbox/permlink/N3MCPAclgAfI63J4), что вам ищем? – skypjack
Да, это тоже похоже на хорошее решение! – Glenn