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

Variadic template argument forwarding uses comma operator

最近我在学习 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)), ...);代替了。该代码与它完美配合。

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

感谢您的回答。

假设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 中你可以阅读更多关于它的信息 Fold Expression

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 和 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)。在这种情况下 (cstd::list) 是多余的,但添加是为了避免返回对象重新定义逗号运算符的潜在问题。