在处理项目时,我遇到一种情况 std::apply 不会转发由 std::forward_as_tuple *IF* 生成的 std::tuple 创建的
存储在变量中!但是,如果 std::forward_as_tuple 结果未存储在变量中,而是作为第二个参数传递给 std::apply ,那么它就可以工作,并且右值引用得到完美转发.std::tuple
的右值引用
我尝试了很多选项,包括对 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/