如何使用 is_base_of 专门化模板而不与主模板混淆?

How can I specialize a template using is_base_of without being ambiguous with the primary template?

使用下面的代码,我收到编译器投诉,称对 get_code 的调用在前两个模板之间不明确。我如何编写代码来检测基数 class 同时提供专门的形式?例如,如果稍后我有 class C : A {} 也应该 return ACLASS.

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

TA

的基础时,您必须停用未知案例
template <typename T>
typename std::enable_if< ! std::is_base_of<A, T>::value, Code>::type
get_code() // -----------^ 
 { return Code::UNKNOWN; }

否则,当 TA 的基础时,编译器可以使用两个版本的 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
 }

-- 编辑 --

另一种可能的解决方案。

如果您可以接受您的函数是模板 class(或 struct)的 static 方法,并且如果您可以接受它被称为 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
 }

添加 static_assert() 是为了避免有人绕过调用类似

的解决方案
gc4<A, false>::func();