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

时间:2018-08-07 00:59:43

标签: c++ templates language-lawyer template-meta-programming

考虑以下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也抱怨:

  

(10):错误C2764:'Types':模板参数未使用或   可以推导部分专业化'Predicate >'

哪种编译器是正确的,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 个答案:

答案 0 :(得分:2)

GCC和MSVC是正确的。

来自[temp.param]/19

  

如果 template-parameter type-parameter 且其可选标识符之前带有省略号,或者是 parameter-declaration 声明包([dcl.fct]),则 template-parameter 是模板参数包。模板参数包是 parameter-declaration ,其类型包含一个或多个未展开的包,即为包扩展。类似地,具有 type-parameter 且包含一个或多个未扩展包的 template-parameter-list 的模板参数包是一个包扩展。 作为包扩展的模板参数包不应扩展在同一template-parameter-list中声明的模板参数包。 [示例:

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
     

-示例]

答案 1 :(得分:0)

在这种情况下,似乎无法推导模板参数。 您可能想尝试使用函数参数,似乎推导规则更容易处理。

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

#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