我希望能够基于结构中类型的存在(无需为每个自定义结构编写任何其他代码)来自定义结构的处理,例如:
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!
答案 0 :(得分:2)
问题是您为void
指定了默认的Enabler
类型,但是T::my_custom_type
不是void
。使用bool
作为默认类型,或使用总是返回std::void_t
的{{1}}:
void
This answer解释了为什么类型应该匹配。
答案 1 :(得分:2)
正如其他人所解释的那样,如果您为第二个模板参数设置了void
默认值,则仅当my_custom_type
为void
时,解决方案才有效。
如果my_custom_type
是bool
,则可以将bool
设置为默认值。但这不是一个很好的解决方案,因为普遍性不强。
更一般地说,如果my_custom_type
不存在,则可以使用SFINAE进行失败,但如果存在void
,则返回相同类型(通常为my_custom_type
)。
在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()
)。
答案 2 :(得分:1)
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
的默认类型,以提出与您的{ {1}}专业化。
实时示例显示它与显式<T, void>
相匹配:https://godbolt.org/z/MEJvwT