C++ 如果在std::apply中使用前将forward_as_元组结果存储在变量中,则会丢失右值引用
在处理项目时,我遇到了这样一种情况:如果生成的std::tuple存储在变量中,则std::apply不会从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::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
bystd::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
asauto 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传递的元组。那就是我所在的地方