模板模板类谓词不适用于部分专业化

时间:2018-11-28 06:38:15

标签: c++ templates c++17 sfinae enable-if

我有许多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

我要自己手动定义类型。我缺少部分专业化和模板模板参数的详细信息吗?

2 个答案:

答案 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());

live on compiler explorer

答案 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 {};

Live Demo

因为在非推论上下文中,Predicate<T>部分不会发生推论,而是将使用获得的PredicateT从其他地方来。

关于通常的检测习惯用法(参见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);
}