代码之家  ›  专栏  ›  技术社区  ›  Curious

模板模板类谓词在部分专用化中不起作用

  •  7
  • Curious  · 技术社区  · 6 年前

    我有很多 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 {};
    

    启用If 像这样的特质

    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/ydemyeroooaoa60jx . 同样的事情是“手动”实现的- https://wandbox.org/permlink/fmcFT3kLSqyiBprm

    我正在求助于自己手动定义类型。我是否缺少部分专门化和模板模板参数的详细信息?

    2 回复  |  直到 6 年前
        1
  •  5
  •   Guillaume Racicot    6 年前

    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

        2
  •  5
  •   llllllllll    6 年前

    Predicate<T>> 在第三个模板中参数是 [temp.alias]/2

    你可以把你的 谓词<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> 相反,它会的 这个 Predicate T 从别处获得的。

    至于为什么通常的检测习惯用法(参见 Guillaume Racicot's answer )会成功的,因为 std::void_t 作为模板别名,将被替换为 void 在扣除阶段(参见 [临时别名]/二 ),因此不会发生扣减。

    下面是一些例子来更清楚地说明这一点:

    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);
    }