C++ 如果在std::apply中使用前将forward_as_元组结果存储在变量中,则会丢失右值引用

C++ 如果在std::apply中使用前将forward_as_元组结果存储在变量中,则会丢失右值引用,c++,tuples,C++,Tuples,在处理项目时,我遇到了这样一种情况:如果生成的std::tuple存储在变量中,则std::apply不会从std::tuple创建的std::forward\u as\u tuple*转发右值引用!但是,如果std::forward\u as_tuple结果没有存储在变量中,而是作为第二个参数传递给std::apply,则它可以工作,并且右值引用可以完全转发 我尝试了许多选项,包括对std::tuple使用不同的类型,如 decltype(auto) t = forward_as_tuple(

在处理项目时,我遇到了这样一种情况:如果生成的std::tuple存储在变量中,则std::apply不会从
std::tuple
创建的
std::forward\u as\u tuple*转发右值引用!但是,如果
std::forward\u 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::\u invoke
by
std::apply…
我的代码有一个godbolt链接:

代码片段

#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)));    
}
#包括
#包括
汽车产品(intl,int&&r){return l*r;}
静态空隙试验不起作用()
{
int r=2;
decltype(auto)t=std::forward_as_tuple(1,std::move(r));
标准::应用(产品,t);
}
静态孔隙试验(工程)
{
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
在/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/functional:54:0中包含的文件中,
发件人:1:
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/tuple:In'constexpr decltype(auto)std::u apply_impl(_Fn&,_tuple&,&,std::index_sequence)[带Fn=int(&)(int,int&);tuple=std::tuple&;长无符号int…Idx={0,1};std::index_sequence=std::integer_sequence]:
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1671:31:必须来自“constexpr decltype(auto)std::apply(_-Fn&&,_-tuple&)[with _-Fn=int(&)(int,int&);_-tuple=std::tuple&]
:10:26:从这里开始需要
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1662:27:错误:调用“\uu invoke(int(&)(int,int&&),int&,int&)”时没有匹配的函数
返回std::_调用(std::forward(_f),
~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
std::get(std::forward(___t))…);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/tuple:41:0中包含的文件中,
从/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/functional:54,
发件人:1:
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5:注:候选:模板constexpr typename std::uu invoke_结果::type std:u invoke(_Callable&&,_Args&&…)
__调用(_可调用&&&&&&&&&&&&…_参数)
^~~~~~~~
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5:注意:模板参数推断/替换失败:
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:替换“模板constexpr typename std::uuuu invoke_uresult::type std::uuu invoke(Callable&,Args&&…[带Callable=int(&)(int,int&);Args={int&,int&}]:
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1662:27:constexpr decltype(auto)std::u apply_impl(Fn&,tuple&,std::index_sequence)[带Fn=int(&)(int,int&);tuple=std::tuple&;长无符号int…Idx={0,1};std::index_sequence=std::integer)
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1671:31:必须来自“constexpr decltype(auto)std::apply(_-Fn&&,_-tuple&)[with _-Fn=int(&)(int,int&);_-tuple=std::tuple&]
:10:26:从这里开始需要
/opt/compiler explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5:错误:在“struct std::\u invoke\u result”中没有名为“type”的类型
返回的编译器:1

当右值引用被传递到
std::forward\u as\u tuple
时,它构造右值引用的
std::tuple
。因此首先,声明
t
as
auto t=std::forward\u as\u tuple(…)
很好,对象的“右值”在
std::forward\u as\u tuple
生成的类型中编码

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

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

另一种可能的修复方法是将
产品
更改为接受纯
int
s的值(在
int
的情况下,没有任何性能影响)。然后,您也可以将
t
作为左值传递

std::apply
perfect将元组转发到
std::get
,以访问元组的元素<如果元组参数是左值,则code>std::get
返回左值引用。由于
t
变量是左值,
std::apply
使用
int&
而不是
int&
调用
product

尽管元组显式地持有一个右值引用,但我无法解释它以这种方式工作背后的动机。但是,要获得与临时变量相同的行为,一个简单的方法是使用
std::move
生成一个xvalue,它将以您想要的方式处理存储的引用:

std::apply(product, std::move(t));  

但是,请注意,与仅使用
t
不同,元组中的任何非引用类型现在都应视为从中移动。我不确定是否有一种简单的方法既可以使用元组的引用类型,也可以将非引用类型视为左值元组的左值。当然,在使用
forward\u as\u tuple
创建元组时,这不会是一个问题,因为它始终包含引用。

这很棘手!感谢您的快速响应!但是,如果元组本身包含右值引用,那么为什么std::apply不能完美地转发第二个参数,即使是从by-Levalue reference传递的元组。那就是我所在的地方