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

问题描述

考虑以下代码

#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)相匹配的专门化语言?

解决方法

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

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

因此,实际上无法通过演绎/专业化获得信息。一般来说,这很好-如果没有此功能,可变参数模板将失去很多功能。

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

,

我们可以通过对0参数实例化的参数进行计数,来确定可以使用0个模板参数实例化的类模板是真正的可变参数还是(仅)所有非变量模板参数都具有默认值:

values

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

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;

对于稍后的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";} }; 测试是必要的(在C ++ 20模式下),我感到有些失望。我认为是something to do with P0522 / CWG150

Demo