c++ - 用 std::forward_as_tuple 模拟 std::forward

标签 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...>> 的引用性的身份转换因为引用与 && 崩溃.

所以如果std::tuple_element_t<Index, tuple<Types...>>评估为 T&返回的类型将是 T& &&这就是 T& . std::tuple_element_t<Index, tuple<Types...>> 的类似原因返回 T&&T

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

最佳答案

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

这是正确的实现方式。

使用应该是这样的:

auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
forward_to_foo( std::move(tuple_args), std::make_index_sequence<sizeof...(args)>{} );

这里有一些不同。

首先,我们通过转发引用获取,而不是左值引用。这让调用者向我们提供右值(prvalue 或 xvalue)元组。

其次,我们将元组转发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 中,它试图匹配 std::apply 的语义,这让你可以用一个更标准的代码。

关于c++ - 用 std::forward_as_tuple 模拟 std::forward,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46060207/

相关文章:

C++ 合并 2 个 Tchar

C++ SFML,轨道

c++ - 如何在 C++ 中将特征张量乘以另一个特征张量的标量和?

c++ - 使用某个宏时如何禁止调用特定函数

c++ - 在 VS2005 中为控制台应用程序分配一个图标(程序代码)

C++ 移动语义说明

c++ - 如何在 constexpr 函数中执行运行时断言?

python - 如何在 Python 中将元组转换为字符串?

Python元组问题

python - 使类可转换为元组和字典