可变参数模板参数转发使用逗号运算符
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)), ...);
代替了。该代码与它完美配合。
这是我不明白的部分。我从未见过这种语法。
- 为什么我的代码不起作用? This answer here 基本都说对了。
- 我找到的正确解决方案实际上有什么作用?我从未使用过逗号运算符,但我发现在使用时,两个表达式都会被求值,而第一个表达式的结果会被丢弃。它在这里做什么,为什么代码没有它就不能工作?
感谢您的回答。
假设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)
。在这种情况下 (c
是 std::list
) 是多余的,但添加是为了避免返回对象重新定义逗号运算符的潜在问题。
最近我在学习 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)), ...);
代替了。该代码与它完美配合。
这是我不明白的部分。我从未见过这种语法。
- 为什么我的代码不起作用? This answer here 基本都说对了。
- 我找到的正确解决方案实际上有什么作用?我从未使用过逗号运算符,但我发现在使用时,两个表达式都会被求值,而第一个表达式的结果会被丢弃。它在这里做什么,为什么代码没有它就不能工作?
感谢您的回答。
假设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)
。在这种情况下 (c
是 std::list
) 是多余的,但添加是为了避免返回对象重新定义逗号运算符的潜在问题。