Jakob Riedle
Jakob Riedle

Reputation: 2018

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)>() );
}

Upvotes: 1

Views: 408

Answers (2)

xskxzr
xskxzr

Reputation: 13040

If the stored value is of type T or T&& (suppose T is an object type), both options yield T&&; if the stored value is of type T&, both options yield T&, so I don't think there is a difference between the two options. I personally like option 2 because forward is usually used along with universal references to forward arguments of a function to another function, while here you are forwarding values stored in a class member, which may be a bit confusing.

Upvotes: 1

Vittorio Romeo
Vittorio Romeo

Reputation: 93304

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.

Upvotes: 2

Related Questions