如何使用 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
当 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
}
-- 编辑 --
另一种可能的解决方案。
如果您可以接受您的函数是模板 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();
使用下面的代码,我收到编译器投诉,称对 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
当 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
}
-- 编辑 --
另一种可能的解决方案。
如果您可以接受您的函数是模板 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();