尝试根据 class 中存在的 typedef 来专门化模板函数
Trying to specialize a template function based on the presence of a typedef within class
我希望能够根据结构中存在的类型来自定义结构的处理(无需为每个自定义结构编写任何额外代码),例如:
struct Normal_t
{
};
struct Custom_t
{
using my_custom_type = bool;
};
好像我应该可以做这样的事情,但它不起作用:
template <class T, typename Enabler = void>
struct has_custom_type
{
bool operator()() { return false; }
};
template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
bool operator()() { return true; }
};
bool b_normal = has_custom_type<Normal_t>()(); // returns false
bool b_custom = has_custom_type<Custom_t>()(); // returns false, INCORRECT? should return true?
我不明白的是,标准库使用了一些类似但看起来更复杂的类型特征。例如,这有效:
template<bool test, class T = void>
struct my_enable_if
{
};
template<class T>
struct my_enable_if<true, T>
{
using type = T;
};
template <class T, class Enabler = void>
struct foo
{
bool operator()() { return false; }
};
template <class T>
struct foo<T, typename my_enable_if<std::is_integral<T>::value>::type>
{
bool operator()() { return true; }
};
bool foo_float = foo<float>()(); // returns false
bool foo_int = foo<int>()(); // returns true
在这两种情况下,专业化都是基于结构中类型的存在而发生的,在一种情况下 typename T::my_custom_type
而在另一种情况下 typename my_enable_if<std::is_integral<T>::value>::type
。为什么第二个版本有效而第一个版本无效?
我使用 ... 参数包语法想出了这个解决方法,但我真的很想了解是否有一种方法可以在不使用参数包语法的情况下使用普通模板特化来做到这一点,如果没有,为什么。
template<typename ...Args>
bool has_custom_type_2(Args&& ...args) { return false; }
template<class T, std::size_t = sizeof(T::my_custom_type)>
bool has_custom_type_2(T&) { return true; }
template<class T, std::size_t = sizeof(T::my_custom_type)>
bool has_custom_type_2(T&&) { return true; } /* Need this T&& version to handle has_custom_type_2(SomeClass()) where the parameter is an rvalue */
bool b2_normal = has_custom_type_2(Normal_t()); // returns false
bool b2_custom = has_custom_type_2(Custom_t()); // returns true - CORRECT!
问题是您为 Enabler
指定了默认 void
类型,但 T::my_custom_type
不是 void
。使用 bool
作为默认类型,或者使用始终 returns void
:
的 std::void_t
template <class T, typename = void>
struct has_custom_type : std::false_type { };
template <class T>
struct has_custom_type<T, std::void_t<typename T::my_custom_type>> : std::true_type { };
This answer 解释了为什么类型应该匹配。
template <class T, typename Enabler = void> // <== void set as default template parameter type
struct has_custom_type
{
bool operator()() { return false; }
};
template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
bool operator()() { return true; }
};
特化在获取模板参数时匹配<T, bool>
。但是,当您仅指定 <T>
,而没有第二个类型时,它会转到您指定的默认类型 =void
以提出调用 <T, void>
,这与您的 bool
专业化。
显示它与显式 <T, bool>
匹配的实例:https://godbolt.org/z/MEJvwT
正如其他人所解释的,如果您为第二个模板参数设置 void
默认值,则您的解决方案仅在 my_custom_type
为 void
时才有效。
如果my_custom_type
为bool
,您可以设置bool
为默认值。但这不是一个很好的解决方案,因为通用性不强。
更一般地说,如果 my_custom_type
不存在但 return 相同类型(void
,通常)如果 my_custom_type
存在。
Pre C++17 您可以使用 decltype()
、std::declval
和逗号运算符的强大功能
template <class T, typename Enabler = void>
struct has_custom_type
{ bool operator()() { return false; } };
template <class T>
struct has_custom_type<T,
decltype( std::declval<typename T::my_custom_type>(), void() )>
{ bool operator()() { return true; } };
从 C++17 开始,它更简单,因为您可以使用 std::void_t
(参见 Evg 的回答,也可以使用 std::true_type
和 std::false_type
而不是定义 operator()
).
我希望能够根据结构中存在的类型来自定义结构的处理(无需为每个自定义结构编写任何额外代码),例如:
struct Normal_t
{
};
struct Custom_t
{
using my_custom_type = bool;
};
好像我应该可以做这样的事情,但它不起作用:
template <class T, typename Enabler = void>
struct has_custom_type
{
bool operator()() { return false; }
};
template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
bool operator()() { return true; }
};
bool b_normal = has_custom_type<Normal_t>()(); // returns false
bool b_custom = has_custom_type<Custom_t>()(); // returns false, INCORRECT? should return true?
我不明白的是,标准库使用了一些类似但看起来更复杂的类型特征。例如,这有效:
template<bool test, class T = void>
struct my_enable_if
{
};
template<class T>
struct my_enable_if<true, T>
{
using type = T;
};
template <class T, class Enabler = void>
struct foo
{
bool operator()() { return false; }
};
template <class T>
struct foo<T, typename my_enable_if<std::is_integral<T>::value>::type>
{
bool operator()() { return true; }
};
bool foo_float = foo<float>()(); // returns false
bool foo_int = foo<int>()(); // returns true
在这两种情况下,专业化都是基于结构中类型的存在而发生的,在一种情况下 typename T::my_custom_type
而在另一种情况下 typename my_enable_if<std::is_integral<T>::value>::type
。为什么第二个版本有效而第一个版本无效?
我使用 ... 参数包语法想出了这个解决方法,但我真的很想了解是否有一种方法可以在不使用参数包语法的情况下使用普通模板特化来做到这一点,如果没有,为什么。
template<typename ...Args>
bool has_custom_type_2(Args&& ...args) { return false; }
template<class T, std::size_t = sizeof(T::my_custom_type)>
bool has_custom_type_2(T&) { return true; }
template<class T, std::size_t = sizeof(T::my_custom_type)>
bool has_custom_type_2(T&&) { return true; } /* Need this T&& version to handle has_custom_type_2(SomeClass()) where the parameter is an rvalue */
bool b2_normal = has_custom_type_2(Normal_t()); // returns false
bool b2_custom = has_custom_type_2(Custom_t()); // returns true - CORRECT!
问题是您为 Enabler
指定了默认 void
类型,但 T::my_custom_type
不是 void
。使用 bool
作为默认类型,或者使用始终 returns void
:
std::void_t
template <class T, typename = void>
struct has_custom_type : std::false_type { };
template <class T>
struct has_custom_type<T, std::void_t<typename T::my_custom_type>> : std::true_type { };
This answer 解释了为什么类型应该匹配。
template <class T, typename Enabler = void> // <== void set as default template parameter type
struct has_custom_type
{
bool operator()() { return false; }
};
template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
bool operator()() { return true; }
};
特化在获取模板参数时匹配<T, bool>
。但是,当您仅指定 <T>
,而没有第二个类型时,它会转到您指定的默认类型 =void
以提出调用 <T, void>
,这与您的 bool
专业化。
显示它与显式 <T, bool>
匹配的实例:https://godbolt.org/z/MEJvwT
正如其他人所解释的,如果您为第二个模板参数设置 void
默认值,则您的解决方案仅在 my_custom_type
为 void
时才有效。
如果my_custom_type
为bool
,您可以设置bool
为默认值。但这不是一个很好的解决方案,因为通用性不强。
更一般地说,如果 my_custom_type
不存在但 return 相同类型(void
,通常)如果 my_custom_type
存在。
Pre C++17 您可以使用 decltype()
、std::declval
和逗号运算符的强大功能
template <class T, typename Enabler = void>
struct has_custom_type
{ bool operator()() { return false; } };
template <class T>
struct has_custom_type<T,
decltype( std::declval<typename T::my_custom_type>(), void() )>
{ bool operator()() { return true; } };
从 C++17 开始,它更简单,因为您可以使用 std::void_t
(参见 Evg 的回答,也可以使用 std::true_type
和 std::false_type
而不是定义 operator()
).