Reputation: 2018
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
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
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