考虑以下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给出了一个错误:
struct Predicate<Head<Values...>> : std::true_type { }; ^~~
MSVC也抱怨:
哪种编译器是正确的,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 { };
答案 0 :(得分:2)
GCC和MSVC是正确的。
如果 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.");
当前,以上内容仅限于具有默认构造函数的类型。 你有一些测试类型吗?
编辑:
可能有一个改进的版本,它不需要将类型默认构造为:
#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.");
您还可以添加类似的内容以使其更加实用:
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.");