假设我使用std::forward_as_tuple
将函数调用的参数存储在元组中
auto args = std::forward_as_tuple(std::forward<Args>(args)...);
然后我通过左值引用将此元组传递给一个函数,该函数想要调用一个函数foo()
,其中args
中的一些参数由另一个std::integer_sequence
确定。我这样做是std::move()
template <typename TupleArgs, std::size_t... Indices>
decltype(auto) forward_to_foo(TupleArgs&& args,
std::index_sequence<Indices...>) {
return foo(std::get<Indices>(std::move(args))...);
}
这样可行,因为std::get<std::tuple>
的右值限定版本返回std::tuple_element_t<Index, tuple<Types...>>&&
,这是std::tuple_element_t<Index, tuple<Types...>>
引用的身份转换,因为引用与{{1}折叠}}。
因此,如果&&
评估为std::tuple_element_t<Index, tuple<Types...>>
,则返回的类型将为T&
,而T& &&
只是T&
。 std::tuple_element_t<Index, tuple<Types...>>
何时返回T&&
和T
我错过了什么吗?在某些情况下,这会失败吗?
答案 0 :(得分:3)
Type Division Cast Coalesce Coalesce with float
Float 0.010000 0.010000 0.010000 0.010000
Integer 0.100000 0.100000 0.100000 0.100000
NULL NULL NULL 0.000000 0.000000
这是正确的实施。
使用应如下所示:
template <typename TupleArgs, std::size_t... Indices>
decltype(auto) forward_to_foo(TupleArgs&& args,
std::index_sequence<Indices...>) {
return foo(std::get<Indices>(std::forward<TupleArgs>(args))...);
}
这里有一些差异。
首先,我们采用转发参考,而不是左值参考。这允许调用者向我们提供rvalue(prvalue或xvalue)元组。
其次,我们将auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
forward_to_foo( std::move(tuple_args), std::make_index_sequence<sizeof...(args)>{} );
调用中。这意味着如果元组移入我们std::get
右值引用。
第三,我们进入get
。这确保了上述做法是正确的。
现在,假设我们想要两次致电forward_to_foo
。
foo
我们根本不必触摸auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
auto indexes = std::make_index_sequence<sizeof...(args)>{};
forward_to_foo( tuple_args, indexes );
forward_to_foo( std::move(tuple_args), indexes );
,我们不会多次从任何forward_to_foo
移动。
对于您的原始实现,对args
的任何调用都会默默地从forward_to_foo
右值引用或值移动而在调用站点没有任何迹象表明我们对第一个参数具有破坏性。
除了那个细节,是模仿转发的。
我自己我只是写一个TupleArgs
:
notstd::apply
然后我们做:
namespace notstd {
namespace details {
template <class F, class TupleArgs, std::size_t... Indices>
decltype(auto) apply(F&& f, TupleArgs&& args,
std::index_sequence<Indices...>) {
return std::forward<F>(f)(std::get<Indices>(std::forward<TupleArgs>(args))...);
}
}
template <class F, class TupleArgs>
decltype(auto) apply(F&& f, TupleArgs&& args) {
constexpr auto count = std::tuple_size< std::decay_t<TupleArgs> >{};
return details::apply(
std::forward<F>(f),
std::forward<TupleArgs>(args),
std::make_index_sequence< count >{}
);
}
}
将棘手的位移到auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
auto call_foo = [](auto&&...args)->decltype(auto){ return foo(decltype(args)(args)...); };
return notstd::apply( call_foo, std::move(tuple_args) );
,它试图匹配notstd::apply
的语义,这样可以用更标准的代码替换它。