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

标签 c++ c++11 templates template-specialization sfinae

使用下面的代码,我收到编译器投诉,称前两个模板之间对 get_code 的调用不明确。我如何编写代码来检测基类,同时还提供专门的形式?例如,如果稍后我有 class C : A {},它也应该返回 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

最佳答案

您必须在 T 时停用未知案例是 A 的基础

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
 }

-- 编辑 --

另一种可能的解决方案。

如果你能接受你的函数是 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
 }

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

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

关于c++ - 如何使用 is_base_of 专门化模板而不与主模板混淆?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42178235/

相关文章:

c++ - 递归 count_files 函数不返回完整结果

c++ - 如何在 Visual Studio 2013 中为函数类型创建类型别名?

c++ - 在模板中使用 typedef 和 typename

c++ - C++ 中的继承和模板

临时地址的 C++ 地址不会导致构建错误

c++ - C++中的最大子序列

c++ - 存储一组指针会导致未定义的行为吗?

c++ - 如何捕获构造函数的原型(prototype)?

c++ - 为什么在将 C 样式数组传递给需要 std::string 的方法时出现链接器问题?

c++ - 引用性与模板函数中的类型无关