我有许多EnableIf
特性,它们基本上检查输入类型是否满足接口要求。我正在尝试创建一个通用的Resolve
特性,该特性可用于将其转换为布尔型特性。
类似的东西-https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
现在,如果您具有EnableIf
这样的特征
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
您可以很快创建一个布尔版本
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
或类似变量模板。
但是由于某些原因,部分专业化无法按预期工作。解决无法正常工作。在此处查看输出-https://wandbox.org/permlink/ydEMyErOoaOa60Jx。 “手动”实施的相同操作有效-https://wandbox.org/permlink/fmcFT3kLSqyiBprm
我要自己手动定义类型。我缺少部分专业化和模板模板参数的详细信息吗?
答案 0 :(得分:5)
我找不到您的示例不起作用的确切原因。如果您想进一步研究std::void_t
的详细信息,请访问interesting explanation
即使我无法深入解释它,我也想添加detection idiom中使用的另一种可靠语法。
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
答案 1 :(得分:5)
您的方法失败的原因是,第三个模板参数中的Predicate<T>>
是不是不可推论的上下文。这将导致推论直接失败(请参见[temp.alias]/2),而不是像在非推论上下文中那样使用其他地方推导的模板参数。
您可以将Predicate<T>>
包装到非推论上下文中以使其起作用:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
因为在非推论上下文中,Predicate<T>
部分不会发生推论,而是将使用获得的Predicate
和T
从其他地方来。
关于通常的检测习惯用法(参见Guillaume Racicot's answer)为何起作用的原因,是因为std::void_t
作为模板别名在推论阶段将被void
取代(参见{ {3}}),因此不会进行扣除。
这里有一些例子可以更清楚地说明这一点:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}