代码之家  ›  专栏  ›  技术社区  ›  Vladimir Reshetnikov

编译类型模板谓词使用Clang编译,但不使用GCC或MSVC

  •  4
  • Vladimir Reshetnikov  · 技术社区  · 8 年前

    考虑下面的C++代码:

    #include <type_traits>
    
    template<int x, int y, bool visible>
    struct Point { };
    
    template<typename T>
    struct Predicate : std::false_type { };
    
    template<typename... Types, Types... Values, template<Types...> class Head>
    struct Predicate<Head<Values...>> : std::true_type { };
    
    static_assert(Predicate<Point<1, 2, true>>::value, "assert");
    

    目的 Predicate 是识别具有任意模板类名的模板实例化(绑定到 Head )存储零个或多个非类型模板参数(可能是不同类型的),而没有其他参数(类型或模板)。此代码使用Clang成功编译,但GCC给出一个错误:

    <源:10:29:错误:扩展模式“<匿名”不包含 参数包

    struct Predicate<Head<Values...>> : std::true_type { }; ^~~

    MSVC还抱怨:

    <source>(10):错误C2764:“类型”:未使用模板参数或 可在“部分专业化”谓词中推导<Head<Values…>'

    哪个编译器是正确的,C++标准在这个主题上说什么?


    我注意到使用C++ 17,可以用这种方式来处理GCC的错误:

    template<auto... Values, template<auto...> class Head>
    struct Predicate<Head<Values...>> : std::true_type { };
    

    但是这个版本仍然有相同的错误:

    template<auto... Values, template<decltype(Values)...> class Head>
    struct Predicate<Head<Values...>> : std::true_type { };
    
    2 回复  |  直到 8 年前
        1
  •  2
  •   xskxzr    8 年前

    GCC和MSVC是正确的。

    从 [temp.param]/19 :

    如果 模板参数 是一个 类型参数 在可选标识符之前加上省略号,或者 参数声明 声明一个包([dcl.fct]),然后 模板参数 是模板参数包。一个模板参数包,它是 参数声明 其类型包含一个或多个未展开的包的是包展开。类似地,模板参数包 类型参数 用一个 模板参数列表 包含一个或多个未展开的包是包展开。 作为包扩展的模板参数包不应扩展在同一模板参数列表中声明的模板参数包。 [示例:

    template <class... Types>                       // Types is a template type parameter pack
       class Tuple;                                 // but not a pack expansion
    
    template <class T, int... Dims>                 // Dims is a non-type template parameter pack
       struct multi_array;                          // but not a pack expansion
    
    template <class... T>
      struct value_holder {
        template <T... Values> struct apply { };    // Values is a non-type template parameter pack
      };                                            // and a pack expansion
    
    template <class... T, T... Values>              // error: Values expands template type parameter
      struct static_array;                          // pack T within the same template parameter list
    

    “结束示例”

        2
  •  0
  •   David Ledger    8 年前

    似乎模板参数在该上下文中不可推导。 您可能想尝试使用函数参数,似乎演绎规则更容易处理。

    类似于这样,如果需要将输出作为类型,可以将其包装在结构上:

    #include <type_traits>
    
    template<int x, int y, bool visible>
    struct Point { };
    
    template <typename T>
    struct NoPoint { };
    
    template <typename T>
    constexpr auto Match(T &&)
    {
        return false;
    }
    
    template<template < auto ... > typename Head, auto ... Values>
    constexpr auto Match(Head<Values...> &&)
    {
        return true;
    }
    
    static_assert(Match(Point<1, 2, true>{}), "I am a wombat.");
    static_assert(Match(NoPoint<int>{}), "I am also a wombat.");
    

    https://godbolt.org/g/fjofbx

    目前,上述内容仅限于具有默认构造函数的类型。 你们有一些测试类型吗?

    编辑:

    一个改进的版本可能是,它不要求类型是默认可构造的:

    #include <type_traits>
    #include <utility>
    
    template<int x, int y, bool visible>
    struct Point { };
    
    template <typename T>
    struct NoPoint { };
    
    template <typename T>
    constexpr auto Match(T) -> std::false_type;
    
    template<template < auto ... > typename Head, auto ... Values>
    constexpr auto Match(Head<Values...>) -> std::true_type;
    
    template <typename T>
    struct Predicate : decltype(Match(std::declval<T>())) {};
    
    static_assert(Predicate<Point<1, 2, true>>::value, "I am a wombat.");
    static_assert(Predicate<NoPoint<int>>::value, "I am not a wombat.");
    

    https://godbolt.org/g/iru2mZ

    您还可以添加类似的内容以使其更可用:

    template <typename T>
    constexpr bool HasValuePack = Predicate<T>::value;
    

    编辑

    我意识到,我应该把这个包含C++变量的模板17变量,这就是定义:

    //Predicate
    template <typename T>
    constexpr bool Predicate_v = false;
    template<template < auto ... > typename Head, auto ... Values>
    constexpr bool Predicate_v<Head<Values...>>   = true;
    

    这些是测试用例

    //Test types
    template<int x, int y, bool visible>
    struct Point { };
    template <typename T>
    struct NoPoint { };
    
    //Test Cases
    static_assert(Predicate_v<Point<1, 2, true>>, "I am a wombat.");
    static_assert(Predicate_v<NoPoint<int>>, "I am not a wombat.");
    

    https://godbolt.org/g/c5gyhH

    推荐文章