Correct usage of `for_each_arg` - too much forwarding?

瘦欲@ 提交于 2019-12-04 13:16:10

Every forward in your code is indeed necessary to perfectly forward all arguments until the end. Names of rvalue references are lvalues, so unless you're forwarding everytime you pass arguments on, the value category information is lost.
Also it is impossible to call forward without an explicit template argument list as the template parameter is only used in one, non-deduced context. In fact, a function template called without an explicit argument list cannot do the job.

You can try a macro to somewhat shorten the code:

#define FORWARD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)

It then becomes

for_each_arg
(
    // Removed superfluous capture
    [&result] (auto&& mX) { 
        result.add(FORWARD(mX));       
    }, 
    FORWARD(mArgs)...
);

It's also possible to use a macro instead of for_each_arg in the first place:

#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)...
);

Just capture & when making this kind of lambda. If you must list, only &result need be captured.

forward<?> is always used with a type parameter.

Note Eric's for_each_arg is imperfect, and mostly about doing it in 140 characters or less. ;) Its imperfections are mild, and harmless here.

Here is an alternative:

First, write this:

template<class...Fs>
void do_in_order(Fs&&...fs){
  int _[]={0,
    (((void)(std::forward<Fs>(fs)())),0)...
  };
  (void)_; // kills warnings
}

it takes zero arg lambdas, and runs them left to right.

Then replace the call to for_each_arg with:

do_in_order(
  [&]{
    result.add(std::forward<TArgs>(mArgs));
  }...
);

the downside is that more compilers won't like the above.

Ordering of the expressions in the do_in_order is guaranteed by [dcl.init] and [dcl.init.list] sections in n4296 8.5.4/4 8.5.4/1 8.5/15 8.5/1. The initialization is a copy-list-initialization (8.5/15 and 8.5.4/1), is a "initializer-list of a braced-init-list" (8.5/1) and as such is sequenced left to right (8.5.4/4).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!