Search code examples
c++move-semanticsperfect-forwardingstdtuple

How to forward the values of a tuple? std::get( move(tuple) ) vs. forward( std::get(tuple) )


Suppose you write something like an "initializer" class, which stores a set of values (including references). Those values are then used to initialize a given type (e.g. through my_initializer.initialize<Foo>():

template<typename... Values>
struct Initializer{
    std::tuple<Values...> values;

    template<typename TypeToInitialize, std::size_t... Is>
    TypeToInitialize initialize_implementation( std::index_sequence<Is...> ){
        return { std::get<Is>( this->values )... };
    }

    template<typename TypeToInitialize>
    TypeToInitialize initialize(){
        return initialize_implementation( std::make_index_sequence<sizeof...(Values)>() );
    }
};

Pretty straightforward sofa. However, now I want to provide an overload of initialize() for rvalue objects that gets called whenever the object it is called on is an rvalue and the call to initialize<...>() is the last action before the object will be destroyed.

How do I forward the values of a tuple? Which Option should I pursue?

template<typename TypeToInitialize, std::size_t... Is>
TypeToInitialize initialize_move_implementation( std::index_sequence<Is...> )
{
    // OPTION 1
    return { std::forward<Values>( std::get<Is>( this->values ) )... };

    // OPTION 2
    return { std::get<Is>( std::move( this->values ) )... };
}

template<typename TypeToInitialize>
TypeToInitialize initialize() && {
    return initialize_move_implementation<TypeToInitialize>( std::make_index_sequence<sizeof...(Values)>() );
}

Solution

  • template<typename TypeToInitialize>
    TypeToInitialize initialize() && {
        return std::make_from_tuple<TypeToInitialize>(this->values);
    }
    

    Internally, it does something like:

    return T(std::get<I>(std::forward<Tuple>(t))...);
    

    Where I is an index sequence like in your original example.