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)?
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.