可变参数模板参数转发使用逗号运算符

Variadic template argument forwarding uses comma operator

本文关键字:参数 运算符 转发 变参      更新时间:2023-10-16

最近我正在学习C++和可变参数模板。我尝试编写一个模板函数,该函数采用容器(我简化了它,并且我只使用list这个问题(和其他一些参数,并将其他参数置换到容器中。我的代码看起来像这样:

#include <iostream>
#include <list>
#include <utility>
template <typename Container, typename... Args>
void my_emplace(Container &c, Args &&... args){
c.emplace_back(std::forward<Args>(args)...);
}
int main(void) {
std::list<int> l = {1, 2};
my_emplace(l, 3, 4, 5);
for (auto i : l)
std::cout << i << ", ";
return 0;
}

但是,代码不起作用:

$ g++ -std=c++17 test.cpp
...
/usr/include/c++/7/ext/new_allocator.h:136:4: error: new initializer expression list treated as compound expression [-fpermissive]
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我在互联网上找到了一个解决方案——c.emplace_back(std::forward<Args>(args)...);行被(c.emplace_back(std::forward<Args>(args)), ...);取代。代码可以很好地工作。

这是我不明白的部分。我从未见过这种语法。

  • 为什么我的代码不起作用?这里的这个答案基本上说它是正确的。
  • 我找到的正确解决方案实际上有什么作用?我从未使用过逗号运算符,但我发现在使用时,两个表达式都会被计算,第一个表达式的结果会被丢弃。它在这里做什么,为什么没有它代码就不起作用?

谢谢你的回答。

假设参数是(1, 2, 3, 4(,这个调用:

c.emplace_back(std::forward<Args>(args)...);

基本上意味着:

c.emplace_back(1, 2, 3, 4);,所以你不能用这些参数构造int来emplace_back因此编译错误。

而这个电话:

(c.emplace_back(std::forward<Args>(args)), ...);

方法

c.emplace_back(1), c.emplace_back(2), c.emplace_back(3), c.emplace_back(4);

第二次调用使用逗号运算符扩展整个表达式,它的调用Fold Expressions它在 C++17 中添加的 您可以阅读更多关于它的信息 折叠表达式

emplace_back会将其参数转发给value_type的构造函数,在本例中为int.这意味着您可以调用c.emplace_back(3),但不能调用c.emplace_back(3,4,5)

传递多个值时,需要多次调用emplace_back

c.emplace_back(3), c.emplace_back(4), c.emplace_back(5);

我们可以在一行上执行此操作,使用上面的逗号运算符。

展开参数包的两种不同方式的工作方式相同。

c.emplace_back(std::forward<Args>(args)...);
// normal un-packing, translates to
c.emplace_back(3, 4, 5);
(c.emplace_back(std::forward<Args>(args)), ...);
// un-packing with a fold-expression using the comma operator
(c.emplace_back(3), c.emplace_back(4), c.emplace_back(5));

从 C++17 开始,您可以使用折叠表达式(参见 super's 和 Gaurav Dhiman 的答案(。

在 C++17(C++11 和 C++14(之前,您可以获得类似的东西 扩展表达式初始化未使用的数组

我的意思是

template <typename Container, typename... Args>
void my_emplace (Container &c, Args && ... args)
{
using unused = int[];
(void)unused { 0, ((void)c.emplace_back(std::forward<Args>(args)), 0)... };
}

观察使用逗号运算符丢弃扩展表达式。

还要观察c.emplace_back()面前的(void)。在这种情况下(c是一个std::list(是多余的,但添加它以避免返回对象重新定义逗号运算符的潜在问题。