【问题标题】:How to match only variadic templates with a template template parameter?如何仅将可变参数模板与模板模板参数匹配?
【发布时间】:2020-11-08 13:28:33
【问题描述】:

考虑以下代码:

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

【问题讨论】:

  • 这样的专业化有什么用?
  • @JeffGarrett 它必须有用吗?它是this question 的后续行动(但我认为它不会增加太多背景信息)。考虑一些为模板参数的不同组合实例化模板的机器,然后你想知道它的数量。我想到了一个模板的测试框架,但我真的没有一个具体的案例。
  • @JeffGarrett 或者更简单。给定一个模板T,编写一个特征,明确确定T 是否是可变参数模板。可以简单地尝试使用不同数量的参数来实例化它,但是默认值的可能性使得很难确定可变性
  • clang 或 gcc 都不能用 c++17 编译它。
  • @Kostas 你知道为什么吗?我将不得不做一些研究,也许会提出另一个问题。然而,foo 的例子对于这个问题来说并不重要,因为无论如何我要求用一些东西来代替 foo

标签: c++ templates variadic-templates template-templates


【解决方案1】:

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

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

因此,信息无法通过演绎/专业化获得。总的来说,这很好 - 如果没有这个功能,可变参数模板会失去很多功能。

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

【讨论】:

    【解决方案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;
    

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

    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 模式下);我想是something to do with P0522 / CWG150

    Demo.

    【讨论】:

    • 这太棒了。这是一个相当学术的问题。我碰壁了,害怕我已经重新审视了我对可变参数模板的整个心理形象。现在我很好奇,你真的用过这样的东西吗?
    • @idclev463035818 我不这么认为;想出一个 0 参数可变参数和默认参数类模板实例化之间的明显差异需要一些思考。也就是说,我熟悉分解类模板实例化的怪癖,但主要是从确定它们是哪个类模板实例化的角度。
    猜你喜欢
    • 2023-03-07
    • 2012-08-08
    • 2023-04-02
    • 2016-12-01
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    相关资源
    最近更新 更多