正确使用“for_each_arg”-转发太多
Correct usage of `for_each_arg` - too much forwarding?
我真的很高兴发现了for_each_arg(...)
,这使得处理参数包变得更加容易。
template<class F, class...Ts> F for_each_arg(F f, Ts&&...a) { return (void)std::initializer_list<int>{(ref(f)((Ts&&)a),0)...}, f; }
但是,我对它的正确用法感到困惑。有许多参数需要完美转发,但我是否执行任何不必要的转发?
阅读代码会因过度的倾斜而变得更加困难。
struct UselessContainer
{
// Expects a perfectly-forwarded item to emplace
template<typename T> void add(T&&) { }
};
// Creates an `UselessContainer` already filled with `mArgs...`
auto makeUselessContainer(TArgs&&... mArgs)
{
using namespace std;
UselessContainer result;
for_each_arg
(
[&result, &mArgs...] // Am I capturing the `mArgs...` pack correctly here?
(auto&& mX) // Am I passing the arguments to the lambda correctly here?
{
// Is this `forward` necessary?
result.add(forward<decltype(mX)>(mX));
// Could it be replaced with
// `result.add(forward(mX));`
// ?
},
forward<TArgs>(mArgs)... // I assume this `forward` is necessary.
);
return result;
}
我所有的问题/疑问都在上面代码示例中的注释中表达。
代码中的每个forward
确实都是必要的,以便将所有参数完美地转发到最后。右值引用的名称是左值,因此除非您每次传递参数时都转发,否则值类别信息将丢失。
此外,如果没有明确的模板参数列表,就不可能调用forward
,因为模板参数仅用于一个非推导上下文。事实上,在没有显式参数列表的情况下调用的函数模板无法完成这项工作。
您可以尝试使用宏来稍微缩短代码:
#define FORWARD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
然后它变成
for_each_arg
(
// Removed superfluous capture
[&result] (auto&& mX) {
result.add(FORWARD(mX));
},
FORWARD(mArgs)...
);
也可以首先使用宏而不是for_each_arg
:
#define FOR_EACH_ARG(...) (void)std::initializer_list<int>{((__VA_ARGS__),0)...}
FOR_EACH_ARG( result.add(forward<TArgs>(mArgs)) );
for_each_arg (
[&](auto&& mX){
result.add(std::forward<decltype(mX)>(mX));
},
std::forward<TArgs>(mArgs)...
);
只需在制作这种 lambda 时捕获&
即可。 如果必须列出,则只需要捕获&result
。
forward<?>
始终与类型参数一起使用。
注意 Eric 的for_each_arg并不完美,主要是用 140 个字符或更少的字符来做。 ;) 它的瑕疵是温和的,在这里是无害的。
这是一个替代方案:
首先,写这个:
template<class...Fs>
void do_in_order(Fs&&...fs){
int _[]={0,
(((void)(std::forward<Fs>(fs)())),0)...
};
(void)_; // kills warnings
}
它需要零参数 lambda,并从左到右运行它们。
然后将对for_each_arg
的调用替换为:
do_in_order(
[&]{
result.add(std::forward<TArgs>(mArgs));
}...
);
缺点是更多的编译器不会喜欢上述内容。
do_in_order
中表达式的顺序由 n4296 8.5.4/4 8.5.4/1 8.5/15 8.5/15 8.5/1 中的 [dcl.init] 和 [dcl.init.list] 部分保证。 初始化是复制列表初始化(8.5/15 和 8.5.4/1),是"大括号初始化列表的初始值设定项列表"(8.5/1),因此从左到右排序(8.5.4/4)。
- 如何在 C 中正确使用 libiconv 使其不会报告"Arg list too long"?
- 正在折叠转发引用
- 将 N-arg 函数包装到另一个函数中
- 将函数参数完美转发到函数指针:按值传递呢?
- 转发变量参数列表以模拟 std::thread
- 在按值调用 (c++) 中转发构造函数参数
- 存储稍后要转发的变量参数
- 如何在 C++ 中转发声明 std::set?
- C++使用默认模板参数键入别名和转发声明
- CNTK:->转发或 ->评估某些电脑上的崩溃,而不是其他电脑上的崩溃
- C++20理念:要求表达和完美转发
- 如何在模板中转发右值和左值引用
- 如何转发声明枚举?
- 使用函数指针转发声明作为 lamba 声明
- CAFFE转发网络在for循环中不起作用
- 如何将迭代器调用转发给类的私有成员?
- 转发复制的 std::tuple
- 完美的转发和构造函数
- 是否可以在不扣除的情况下将模板参数转发到 make_*?
- 通过基类接受方法转发派生 UniquePtr 的右值会移动引用而不是复制