我很高兴发现for_each_arg(...)
,这使处理参数包变得更加容易。
for_each_arg(...)
但是,我对其正确用法感到困惑。有很多参数需要完美地转发,但是我是否执行了不必要的转发?
由于过度追求而难以阅读代码。
template<class F, class...Ts>
F for_each_arg(F f, Ts&&...a) {
return (void)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)) );
仅在制作这种Lambda时捕获for_each_arg (
[&](auto&& mX){
result.add(std::forward<decltype(mX)>(mX));
},
std::forward<TArgs>(mArgs)...
);
。如果必须列出,则仅需要捕获&
。
[&result
始终与类型参数一起使用。
注意Eric的for_each_arg是不完善的,并且大多数情况下是用140个字符或更少的字符来完成的。 ;)它的缺点是轻微的,在这里无害。
这里是替代方法:
首先,写这个:
forward<?>
它需要零个arg lambda,并从左到右运行它们。
然后将对template<class...Fs>
void do_in_order(Fs&&...fs){
int _[]={0,
(((void)(std::forward<Fs>(fs)())),0)...
};
(void)_; // kills warnings
}
的呼叫替换为:
for_each_arg
缺点是更多的编译器不喜欢上面的。
do_in_order(
[&]{
result.add(std::forward<TArgs>(mArgs));
}...
);
中表达式的顺序由n4296 8.5.4 / 4 8.5.4 / 1 8.5 / 15 8.5 / 1中的[dcl.init]和[dcl.init.list]节保证。初始化是一个复制列表初始化(8.5 / 15和8.5.4 / 1),是一个bracket-init-list的“ initializer-list”(8.5 / 1),并且这样的顺序是从左到右(8.5.4 / 4)。
我的建议:
do_in_order
[还有选择如何将笛卡尔积线性化/序列化为元组序列(“ writeme”部分)。