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

for*_*818 5 c++ templates template-templates variadic-templates

考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出是(gcc10.2@godbolt):

single param case
two param case
base case
base case
Run Code Online (Sandbox Code Playgroud)

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

是否可以编写仅与可变参数模板匹配的专业化(barN在上面的示例中)?

eca*_*mur 2

我们可以通过计算 0 参数实例化的参数来确定可以使用 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;
Run Code Online (Sandbox Code Playgroud)

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

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";}
};
Run Code Online (Sandbox Code Playgroud)

我有点失望的是后面的argc_v测试是必要的(在 C++20 模式下);我认为这与 P0522 / CWG150 有关

演示