如何仅将可变参数模板与模板模板参数匹配?

How to match only variadic templates with a template template parameter?

考虑以下代码:

#include <iostream>

template <template<class...> class C>
struct foo {
    foo() { std::cout << "base case\n";}
};

template <template<class> class C>
struct foo< C > {
    foo() { std::cout << "single param case\n";}
};

template <template<class,class> class C>
struct foo< C > {
    foo() { std::cout << "two param case\n";}
};

template <typename T> struct bar1 {};
template <typename T,typename U> struct bar2 {};
template <typename T,typename U,typename V> struct bar3 {};
template <typename...T> struct barN {};

int main() {
    foo<bar1> f;
    foo<bar2> g;
    foo<bar3> h;
    foo<barN> n;
}

输出为 (gcc10.2@godbolt):

single param case
two param case
base case
base case

假设给定了 barX 并且我有其他具有不同数量类型参数的模板。有些可变参数有些不是。

是否可以编写仅匹配可变参数模板的特化(上例中的barN)?

非常有趣的问题。不幸的是,答案是否定的。 没有通用的方法来确定模板是否具有模板参数包或只是一堆带或不带默认值的常规模板参数。

原因是非可变模板可以绑定到可变模板模板参数,模板的具体类型可以绑定到模板参数包。

因此实际上无法通过 deduction/specialization 获得信息。总的来说,这很好——如果没有这个特性,可变参数模板将失去它们的大部分功能。

但是如果我们可以限制模板参数的最大长度,我们就可以编写一个具有大量模板特化的特征。这是因为部分排序(如您在问题中所示):godbolt

我们可以确定一个可以用 0 个模板参数实例化的 class 模板是真正的可变参数还是(仅仅)所有非可变模板参数都有默认值,方法是将参数计数为 0-参数实例化:

template<class> constexpr unsigned argc_v;
template<template<class...> class C, class... A> constexpr unsigned argc_v<C<A...>> = sizeof...(A);
template<template<class...> class, class = void> constexpr bool is_variadic_v = false;
template<template<class...> class C> constexpr bool is_variadic_v<C, std::void_t<C<>>> = argc_v<C<>> == 0;

然后我们可以使用它来构建一组专门化,分别只接受可变参数、1 参数(可能有默认值)和 2 参数(可能有 default/s)class 模板:

template<template<class...> class, class = std::true_type>
struct foo;

template<template<class...> class C>
struct foo<C, std::bool_constant<is_variadic_v<C>>> {
    foo() { std::cout << "variable case\n"; }
};

template<template<class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void>> == 1>> {
    foo() { std::cout << "single param case\n";}
};

template<template<class, class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void, void>> == 2>> {
    foo() { std::cout << "two param case\n";}
};

我有点失望,后者 argc_v 测试是必要的(在 C++20 模式下);我认为是 .

Demo.