使用std :: forward_as_tuple模拟std :: forward

时间:2017-09-05 17:12:46

标签: c++ c++11 tuples forwarding

假设我使用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

的类似原因

我错过了什么吗?在某些情况下,这会失败吗?

1 个答案:

答案 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的语义,这样可以用更标准的代码替换它。