如何使用is_base_of专门化模板,而不会与主模板产生歧义

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

本文关键字:歧义 of base 专门化 何使用 is      更新时间:2023-10-16

使用下面的代码,我收到编译器抱怨对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

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();