Warm tip: This article is reproduced from serverfault.com, please click

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

发布于 2020-11-08 13:28:33

Consider the following code:

#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;
}

Output is (gcc10.2@godbolt):

single param case
two param case
base case
base case

Suppose barX is given and that I have other templates with varying number of type parameters. Some variadic some not.

Is it possible to write a specialization that only matches the variadic template (barN in the above example)?

Questioner
largest_prime_is_463035818
Viewed
0
ecatmur 2020-11-29 23:35:31

We can determine whether a class template that can be instantiated with 0 template arguments is genuinely variadic or (merely) has defaults for all its non-variadic template arguments, by counting the arguments to an 0-argument instantiation:

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;

Then we can use this to build a set of specializations that respectively accept only variadic, 1-argument (with possible default) and 2-argument (with possible default/s) class templates:

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";}
};

I'm a bit disappointed that the latter argc_v tests are necessary (in C++20 mode); I think it's something to do with P0522 / CWG150.

Demo.