Имея дело с Rvalue Ref внутри кортежа - PullRequest
4 голосов
/ 11 октября 2019

Я хочу передать и переслать кортежи, содержащие значение r refue. Но я не могу получить доступ к элементу кортежа и выполнить переадресацию правильным способом.

В этом примере я также предоставляю структуру с именованными элементами вместо 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
}

Я знаю, что могу использовать ссылку на пересылку для самого кортежа. Это будет работать нормально, но это невозможно в моем коде, так как я намеренно должен иметь некоторое дублирование данных внутри него, которое не будет работать, если я перенаправлю сам кортеж по ссылке rvalue. Если бы я мог использовать это, я просто могу использовать make_from_tuple .

Ответы [ 2 ]

4 голосов
/ 11 октября 2019

std::get возвращает ссылку на объект при работе с кортежем. Поэтому, даже если это rvalue-ref, тип возвращаемого значения std::get будет lvalue-ref.

Чтобы получить тип из кортежа, используйте 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)>{});
}
1 голос
/ 11 октября 2019

Ваша функция func копирует значения в кортеже, поскольку они не могут быть перемещены, так как ваш кортеж const:

// -----v
Y func( const std::tuple< PARMS... >& t ) 

При вызове get, он возвратит ссылку на член, и так какэто постоянный кортеж, это будет постоянная ссылка, которая будет скопирована.

Но, std::get также выполняет некоторую пересылку. Например, если вы отправите std::tuple<T>& на std::get, он вернет T&. Если вы хотите переадресацию, в вашем случае вам придется переместить кортеж:

template<typename ... PARMS>
Y func(std::tuple<PARMS...> t) 
{   
    return Y{std::get<0>(std::move(t))};
}

Тогда, в качестве бонуса, вам не нужен форвард, так как переадресация выполняется std::get.

...