处理元组中的右值引用
dealing with rvalue ref inside tuple
我想传递和转发包含右值引用的元组。但是我无法访问元组元素并以正确的方式进行转发。
在示例中,我还提供了一个具有命名元素的结构,而不是 std::tuple
,效果很好。
所以我自己根本找不到答案如何写那一行:
return Y{ std::forward<decltype(std::get<0>(t))>( std::get<0>( t ) ) };
这样我就得到了正确的转发类型。
完整示例:
#include <iostream>
#include <tuple>
class X
{
private:
int data;
public:
X( int _data ): data{_data}{}
};
class Y
{
public:
Y( const X& ) { std::cout << "copy" << std::endl; }
Y( X&& ) { std::cout << "move" << std::endl; }
};
struct Sm { X&& x; };
struct Sc { X& x; };
template < typename AnyS >
Y func( const AnyS& s )
{
return Y{ std::forward<decltype(s.x)>(s.x) };
}
template < typename ... PARMS >
Y func( const std::tuple< PARMS... >& t )
{
return Y{ std::forward<decltype(std::get<0>(t))>( std::get<0>( t ) ) };
}
int main()
{
Sm sm{ X{1} };
Y ym = func( sm ); // should move, fine works
X x{1};
Sc sc{ x };
Y yc = func( sc ); // should copy, fine works
Y ytm = func( std::tuple< X& >{ x }); // should copy, works
Y ytc = func( std::tuple< X&& >{ X{1} }); // should move but copies! FAIL
}
我知道我可以对元组本身使用转发引用。这可以正常工作,但在我的代码中是不可能的,因为我有意需要在其中进行一些数据复制,如果我通过右值引用转发元组本身,这将不起作用。如果我可以使用它,我可以简单地使用 make_from_tuple.
std::get
returns 处理元组时对对象的引用。因此,即使它是右值引用,std::get
的 return 类型也将是左值引用。
要从元组中获取类型,请改用 std::tuple_element
。
return Y{ std::forward<std::tuple_element_t<0, std::tuple<PARMS...>>>( std::get<0>( t ) ) };
编辑:
如果我理解你的评论是正确的,你应该能够通过别名元组类型来扩展它。
template <typename ... PARMS, std::size_t... Is>
Y func(const std::tuple<PARMS...>& t, std::index_sequence<Is...>)
{
using tt = std::tuple<PARMS...>;
return (Y{ std::forward<std::tuple_element_t<Is, tt>>( std::get<Is>( t ) ) }, ...);
}
template < typename ... PARMS, int... Is >
Y func( const std::tuple< PARMS... >& t )
{
return func(t, std::make_index_sequence<sizeof...(PARMS)>{});
}
您的函数 func
复制元组内的右值,因为您的元组是常量,因此无法将它们移出:
// -----v
Y func( const std::tuple< PARMS... >& t )
调用get时,会return成员的引用,因为是常量元组,所以是常量引用,会被复制。
但是,std::get
也做一些转发。例如,如果你发送一个 std::tuple<T>&
到 std::get
,它将 return 发送一个 T&
。如果你想转发,在你的情况下你必须移动元组:
template<typename ... PARMS>
Y func(std::tuple<PARMS...> t)
{
return Y{std::get<0>(std::move(t))};
}
然后,作为奖励,您不需要转发,因为转发是由 std::get
完成的。
我想传递和转发包含右值引用的元组。但是我无法访问元组元素并以正确的方式进行转发。
在示例中,我还提供了一个具有命名元素的结构,而不是 std::tuple
,效果很好。
所以我自己根本找不到答案如何写那一行:
return Y{ std::forward<decltype(std::get<0>(t))>( std::get<0>( t ) ) };
这样我就得到了正确的转发类型。
完整示例:
#include <iostream>
#include <tuple>
class X
{
private:
int data;
public:
X( int _data ): data{_data}{}
};
class Y
{
public:
Y( const X& ) { std::cout << "copy" << std::endl; }
Y( X&& ) { std::cout << "move" << std::endl; }
};
struct Sm { X&& x; };
struct Sc { X& x; };
template < typename AnyS >
Y func( const AnyS& s )
{
return Y{ std::forward<decltype(s.x)>(s.x) };
}
template < typename ... PARMS >
Y func( const std::tuple< PARMS... >& t )
{
return Y{ std::forward<decltype(std::get<0>(t))>( std::get<0>( t ) ) };
}
int main()
{
Sm sm{ X{1} };
Y ym = func( sm ); // should move, fine works
X x{1};
Sc sc{ x };
Y yc = func( sc ); // should copy, fine works
Y ytm = func( std::tuple< X& >{ x }); // should copy, works
Y ytc = func( std::tuple< X&& >{ X{1} }); // should move but copies! FAIL
}
我知道我可以对元组本身使用转发引用。这可以正常工作,但在我的代码中是不可能的,因为我有意需要在其中进行一些数据复制,如果我通过右值引用转发元组本身,这将不起作用。如果我可以使用它,我可以简单地使用 make_from_tuple.
std::get
returns 处理元组时对对象的引用。因此,即使它是右值引用,std::get
的 return 类型也将是左值引用。
要从元组中获取类型,请改用 std::tuple_element
。
return Y{ std::forward<std::tuple_element_t<0, std::tuple<PARMS...>>>( std::get<0>( t ) ) };
编辑: 如果我理解你的评论是正确的,你应该能够通过别名元组类型来扩展它。
template <typename ... PARMS, std::size_t... Is>
Y func(const std::tuple<PARMS...>& t, std::index_sequence<Is...>)
{
using tt = std::tuple<PARMS...>;
return (Y{ std::forward<std::tuple_element_t<Is, tt>>( std::get<Is>( t ) ) }, ...);
}
template < typename ... PARMS, int... Is >
Y func( const std::tuple< PARMS... >& t )
{
return func(t, std::make_index_sequence<sizeof...(PARMS)>{});
}
您的函数 func
复制元组内的右值,因为您的元组是常量,因此无法将它们移出:
// -----v
Y func( const std::tuple< PARMS... >& t )
调用get时,会return成员的引用,因为是常量元组,所以是常量引用,会被复制。
但是,std::get
也做一些转发。例如,如果你发送一个 std::tuple<T>&
到 std::get
,它将 return 发送一个 T&
。如果你想转发,在你的情况下你必须移动元组:
template<typename ... PARMS>
Y func(std::tuple<PARMS...> t)
{
return Y{std::get<0>(std::move(t))};
}
然后,作为奖励,您不需要转发,因为转发是由 std::get
完成的。