c++ - 如果在 std::apply 中使用前向 as_tuple 结果存储在变量中,则丢失右值引用

标签 c++ tuples

在处理项目时,我遇到一种情况 std::apply 不会转发由 std::forward_as_tuple *IF* 生成的 std::tuple 创建的 std::tuple 的右值引用 存储在变量中!但是,如果 std::forward_as_tuple 结果未存储在变量中,而是作为第二个参数传递给 std::apply ,那么它就可以工作,并且右值引用得到完美转发.

我尝试了很多选项,包括对 std::tuple 使用不同类型,例如

decltype(auto) t = forward_as_tuple(1, std::move(r))
auto t = forward_as_tuple(1, std::move(r))
auto&& t = forward_as_tuple(1, std::move(r))
auto& t = forward_as_tuple(1, std::move(r))

没有什么可以帮助将元组存储在变量中,然后将其传递给 std::apply。看起来左值引用最后被 std::apply... 转发到 std::__invoke 我的代码有一个 godbolt 链接:https://godbolt.org/z/24LYP5

代码片段

#include <functional>
#include <iostream>

auto product(int l, int&& r) {  return l * r; }

static void test_not_works()
{
    int r = 2;
    decltype(auto) t = std::forward_as_tuple(1, std::move(r));
    std::apply(product, t);    
}

static void test_works()
{
    int r = 2;    
    std::apply(product, std::forward_as_tuple(1, std::move(r)));    
}

错误信息

In file included from /opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/functional:54:0,

                 from <source>:1:

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple: In instantiation of 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]':

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1671:31:   required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&]'

<source>:10:26:   required from here

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1662:27: error: no matching function for call to '__invoke(int (&)(int, int&&), int&, int&)'

       return std::__invoke(std::forward<_Fn>(__f),

              ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~

       std::get<_Idx>(std::forward<_Tuple>(__t))...);

       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In file included from /opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:41:0,

                 from /opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/functional:54,

                 from <source>:1:

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5: note: candidate: template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)

     __invoke(_Callable&& __fn, _Args&&... __args)

     ^~~~~~~~

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5: note:   template argument deduction/substitution failed:

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h: In substitution of 'template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = int (&)(int, int&&); _Args = {int&, int&}]':

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1662:27:   required from 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]'

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1671:31:   required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&]'

<source>:10:26:   required from here

/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5: error: no type named 'type' in 'struct std::__invoke_result<int (&)(int, int&&), int&, int&>'

Compiler returned: 1

最佳答案

当右值引用传递给 std::forward_as_tuple 时,它会构造一个右值引用的 std::tuple。因此,首先,将 t 声明为 auto t = std::forward_as_tuple(...) 就可以了,对象的“右值”被编码在生成的类型中通过std::forward_as_tuple

但请注意,std::apply 的第一个参数有一些特殊之处:product 按值获取 int 参数,第二个是 int&&,即右值引用。调用这样的函数显然需要第二个参数是右值引用,但这只有在确保它是右值引用时才有效。因此:

auto t = std::forward_as_tuple(1, std::move(r));
std::apply(product, std::move(t));
                   // ^^^^^^^ cast to rvalue here

另一个可能的修复方法是将 product 更改为按值接受普通 int(如果是 int,则没有性能无论如何都打)。然后,您也可以将 t 作为左值传递。

关于c++ - 如果在 std::apply 中使用前向 as_tuple 结果存储在变量中,则丢失右值引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56731501/

相关文章:

c++ - FIFO 类型的容器 - 哪种 STL 容器最合适,为什么?

c++ - 链接 .lib 文件 : fatal error LNK1107

c++ - STL 按变量排序,然后按升序排序

haskell - 在 Haskell 中操作元组

f# - F# 是否从后向前迭代元组?

c++ - 为什么 std::tie 没有标记为 C++14 的 constexpr?

c++ - 元组是否有隐含的字典序比较?

c++ - 为什么 RegisterClass 因 ERROR_NOT_ENOUGH_MEMORY 而失败?

python - 当 python 使用 "Python.h"调用该 c++ 进程时,如何在 python 中停止 c++ 进程

ios - 创建元组数组以调用函数但无法获得返回