模板模板类谓词不在部分特化中工作

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

我自己手动定义类型.是否存在我缺少的部分特化和模板模板参数的详细信息?

Gui*_*cot 5

我找不到你的例子不起作用的确切原因.如果你想深入了解细节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)

住在编译器资源管理器上


lll*_*lll 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 {};
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)