匹配可变参数非类型模板

And*_*dyG 21 c++ templates non-type variadic-templates c++14

假设我有两个结构,Foo并且Bar:

template<int...>
struct Foo{};

template<unsigned long...>
struct Bar{};
Run Code Online (Sandbox Code Playgroud)

我想创建一个类型特征(调用它match_class),如果我传递两种Foo<...>类型或两种Bar<...>类型,则返回true ,但如果我尝试混合它们则返回false:

int main()
{
    using f1 = Foo<1, 2, 3>;
    using f2 = Foo<1>;
    using b1 = Bar<1, 2, 3>;
    using b2 = Bar<1>;
    static_assert(match_class<f1, f2>::value, "Fail");
    static_assert(match_class<b1, b2>::value, "Fail");
    static_assert(!match_class<f1, b1>::value, "Fail");
}
Run Code Online (Sandbox Code Playgroud)

对于C++ 1z(clang 5.0.0和gcc 8.0.0),它就足够了(Demo):

template<class A, class B>
struct match_class : std::false_type{};

template<class T, template<T...> class S, T... U, T... V>
struct match_class<S<U...>, S<V...>> : std::true_type{};
Run Code Online (Sandbox Code Playgroud)

但在C++ 14中我得到以下错误(相同的编译器* 演示):

error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct match_class<S<U...>, S<V...>> : std::true_type{};
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: non-deducible template parameter 'T'
template<class T, template<T...> class S, T... U, T... V>
Run Code Online (Sandbox Code Playgroud)

问题:C++ 14中的解决方法是什么?

理想情况下,测试类型特征的语法应保持不变.

次要问题:C++ 14的行为是否正确?(或者我看到的C++ 17未指定的行为?)

*请注意,MSVC 19.00.23506具有相同类型的故障演示

Bar*_*rry 17

在C++ 14中,您无法推断出T:

template<class T, template<T...> class S, T... U, T... V>
struct match_class<S<U...>, S<V...>> : std::true_type{};
Run Code Online (Sandbox Code Playgroud)

但在C++ 17中,你可以.你看到的行为是正确的.

在C++ 14中,由于无法推断T,因此需要一种明确提供它的方法.因此,您可能需要类模板本身来指示其非类型模板参数类型:

template <int...> struct Foo { using type = int; };
template <unsigned long...> struct Bar { using type = unsigned long; };
Run Code Online (Sandbox Code Playgroud)

或者有一个外部特征.然后显式写出所有内容 - 两个类模板匹配,如果它们具有相同的非类型模板参数,然后按顺序具有相同的类模板:

template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;

template <class T1, class T2, class A, class B>
struct match_class_impl : std::false_type { };

template <class T, template <T...> class S, T... U, T... V>
struct match_class_impl<T, T, S<U...>, S<V...>> : std::true_type{};

template <class A, class B, class=void>
struct match_class : std::false_type { };

template <class A, class B>
struct match_class<A, B, void_t<typename A::type, typename B::type>>
    : match_class_impl<typename A::type, typename B::type, A, B>
{ };
Run Code Online (Sandbox Code Playgroud)

这是添加支持的结果template auto.在C++ 14中,[temp.deduct.type]包含:

无法从非类型模板参数的类型推导出模板类型参数.[例:

template<class T, T i> void f(double a[10][i]);
int v[10][20];
f(v); // error: argument for template-parameter T cannot be deduced
Run Code Online (Sandbox Code Playgroud)

- 末端的例子]

但是在C++ 17中,它现在是:

P从表达式推导出与从属类型声明的非类型模板参数对应的参数的值时,从值的类型推导出类型中的模板参数P.[例如:

template<long n> struct A { };

template<typename T> struct C;
template<typename T, T n> struct C<A<n>> {
  using Q = T;
};

using R = long;
using R = C<A<2>>::Q;           // OK; T was deduced to long from the
                                // template argument value in the type A<2>
Run Code Online (Sandbox Code Playgroud)

- 结束示例]类型N中的类型T[N]std?::?size_­t.[例如:

template<typename T> struct S;
template<typename T, T n> struct S<int[n]> {
  using Q = T;
};

using V = decltype(sizeof 0);
using V = S<int[42]>::Q;        // OK; T was deduced to std?::?size_­t from the type int[42]
Run Code Online (Sandbox Code Playgroud)

- 结束例子]


sky*_*ack 6

问题:C++ 14中的解决方法是什么?

C++ 14中可能的解决方法基于特征.
作为一个最小的,工作的例子(甚至可能是愚蠢的,但它有助于获得想法):

#include <type_traits>
#include <utility>

template<int...>
struct Foo{};

template<unsigned long...>
struct Bar{};

template<typename>
struct traits;

template<int... V>
struct traits<Foo<V...>> { using type = Foo<0>; };

template<unsigned long... V>
struct traits<Bar<V...>> { using type = Bar<0>; };

template<typename T, typename U>
constexpr bool match = std::is_same<typename traits<T>::type, typename traits<U>::type>::value;

int main() {
    using f1 = Foo<1, 2, 3>;
    using f2 = Foo<1>;
    using b1 = Bar<1, 2, 3>;
    using b2 = Bar<1>;

    static_assert(match<f1, f2>, "Fail");
    static_assert(match<b1, b2>, "Fail");
    static_assert(!match<f1, b1>, "Fail");
}
Run Code Online (Sandbox Code Playgroud)

作为旁注,在C++ 17中,您可以简化以下内容:

template<template<auto ...> class S, auto... U, auto... V>
struct match_class<S<U...>, S<V...>> : std::true_type{};
Run Code Online (Sandbox Code Playgroud)

关于错误背后的原因,@ Barry的答案包含了解你需要的所有内容(像往常一样).