Cur*_*ous 7 c++ templates sfinae enable-if c++17
我有很多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 {};
Run Code Online (Sandbox Code Playgroud)
现在,如果你有这样的EnableIf特质
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
Run Code Online (Sandbox Code Playgroud)
您可以非常快速地创建它的布尔版本
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Run Code Online (Sandbox Code Playgroud)
或类似的变量模板.
但由于某种原因,部分专业化没有按预期工作.Resolve不能按预期工作.请参阅此处的输出 - https://wandbox.org/permlink/ydEMyErOoaOa60Jx.同样的事情实现"手动"工作 - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
我自己手动定义类型.是否存在我缺少的部分特化和模板模板参数的详细信息?
我找不到你的例子不起作用的确切原因.如果你想深入了解细节std::void_t,这里有一个有趣的解释
即使我无法深入解释,我还想添加另一种可靠的语法,用于检测习语.
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());
Run Code Online (Sandbox Code Playgroud)
您的方法失败的原因是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 {};
Run Code Online (Sandbox Code Playgroud)
由于非推测的上下文中,扣除将不会发生Predicate<T>部分,相反,它会使用的Predicate,并T从其他地方获得.
至于为什么通常的检测习语(参见Guillaume Racicot的答案)会起作用,那是因为std::void_t作为模板别名,将void在演绎阶段(参见[temp.alias]/2)中取代,因此不会发生演绎.
以下是一些示例,可以更清楚地说明:
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);
}
Run Code Online (Sandbox Code Playgroud)