【问题标题】:Recursive variadic void function using template params in C++11在 C++11 中使用模板参数的递归可变参数 void 函数
【发布时间】:2018-08-19 16:31:11
【问题描述】:

我愿意做这么简单的事情:

template <typename I, typename... In>
void bar() {
    // Use I here
    bar<In...>(); // Enable recursion
}

但是,这会导致“对重载函数的模糊调用”。 让我好奇的是下面的代码有效:

template <typename T = void>
void foo() { }

template <int T, int... Tn>
void foo() {
    foo<Tn...>();
}

这是为什么呢? 以下方法也有效:

void foobar() {}

template <typename I, typename... In>
void foobar(I i, In... in) {
    foobar(in...);
}

那么,鉴于此函数签名不使用任何 BRACED-INIT-LIST 技术,实现递归的最简单方法是什么:

template <typename I, typename... In>
void bar();

【问题讨论】:

  • 你需要在某个时候结束递归,对吧?这就是为什么你必须声明空参数列表的原因。
  • 第一个不起作用,因为它最终会尝试调​​用bar&lt;&gt;();
  • “braced-init-list 技术”是什么意思?我在您的问题中没有看到带括号的初始化列表。
  • @HolyBlackCat 感谢您的回复。可以使用一个支撑初始化列表来解决我的问题,这对我来说不是一个解决方案。
  • 现在我很好奇。你能展示这样的解决方案吗?

标签: c++ c++11 templates recursion variadic-templates


【解决方案1】:

你可以用同样的方法解决foo()solve:当Tn...列表为空时,foo&lt;&gt;()被调用,所以foo()版本带有type模板参数(但是使用默认值)被调用(编译器将foo&lt;&gt;()foo&lt;void&gt;()匹配)。

对于bar(),您可以切换整数和类型。

您可以添加一个接收具有默认值的非类型模板参数的终端版本

template <int = 0>
void bar()
 { }

因此,当您使用空的 In... 类型列表调用 bar() 时,编译器会将调用 bar&lt;&gt;()bar&lt;0&gt;() 匹配。

--- 编辑---

OP 询问

如果foo&lt;&gt;()匹配foo&lt;void&gt;(),为什么bar&lt;&gt;()不匹配bar&lt;void&gt;()template &lt;typename Dummy = void&gt;而不是template &lt;int = 0&gt;)?换句话说,为什么它必须是template &lt;int = 0&gt;(@ 987654340@类型)?

首先,为什么foo() 有效?

因为有一个接收一个或多个整数的foo() 可变参数模板函数和一个接收单一类型(默认)的foo() 模板。

所以,当你调用foo&lt;&gt;时,它不匹配整数可变参数版本(因为至少需要一个整数)并且匹配类型版本(激活void默认类型)。

第二:为什么bar() 在添加接收单一(和默认)类型的版本时不起作用?

因为您有一个接收一种或多种类型的可变参数模板函数

template <typename I, typename... In>
void bar ()
 { bar<In...>(); }

所以如果你添加一个接收一种类型的版本(默认)

template <typename = void>
void bar ()
 { }

您遇到了编译器问题:当您使用最后一种类型调用 bar() 时选择哪个版本?

我的意思是:你打电话给bar&lt;int, long&gt;(),只有可变参数版本匹配。

但可变参数版本调用bar&lt;long&gt;()

现在的问题是bar() 的两个版本(可变参数版本和单一类型的版本)都匹配。

所以编译器给你一个错误。

诀窍是创建一个带有单个默认模板参数的bar() 版本,该模板参数可以匹配空列表调用 (foo&lt;&gt;()),但不会与类型可变参数版本冲突。

一种可能的解决方案是int,但您可以选择其他类型(charlongunsigned long long 等),甚至可以选择基于模板模板默认参数的版本。

我的意思是......而不是

template <int = 0>
void bar ()
 { }

你可以使用

template <template <typename...> class = std::vector>
void bar()
 { }

这不是建议:我认为int = 0 更易于编写和理解。只是为了展示另一种可能的方式。

重点是添加一个接收单个默认值但不接收类型名称的模板。

【讨论】:

  • @HolyBlackCat - 我同意:我不理解“braced-init-list”部分。
  • 如果foo&lt;&gt;()匹配foo&lt;void&gt;(),为什么bar&lt;&gt;()不匹配bar&lt;void&gt;()template &lt;typename Dummy = void&gt;而不是template &lt;int = 0&gt;)?换句话说,为什么它必须是template &lt;int = 0&gt;(int 类型)?它看起来像一个丑陋的黑客和令人困惑的理解
  • @YvesHenri - 修改答案试图解释这一点(我认为这是一个很棒的黑客,我不知道;谢谢)
猜你喜欢
  • 2012-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-14
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多