paper
paper

Reputation: 165

Variadic template question in piece-wise constructor of std::pair

// TEMPLATE CONSTRUCTOR pair::pair(tuple, tuple, sequence, sequence)
template<class _Ty1,
    class _Ty2>
    template<class _Tuple1,
        class _Tuple2,
        size_t... _Indexes1,
        size_t... _Indexes2> inline
        pair<_Ty1, _Ty2>::pair(_Tuple1& _Val1,
            _Tuple2& _Val2,
            index_sequence<_Indexes1...>,
            index_sequence<_Indexes2...>)
        : first(_STD get<_Indexes1>(_STD move(_Val1))...),
            second(_STD get<_Indexes2>(_STD move(_Val2))...)
        {   // construct from pair of tuples
        }

// TEMPLATE CONSTRUCTOR pair::pair(piecewise_construct_t, tuple, tuple)
template<class _Ty1,
    class _Ty2>
    template<class... _Types1,
        class... _Types2> inline
        pair<_Ty1, _Ty2>::pair(piecewise_construct_t,
            tuple<_Types1...> _Val1,
            tuple<_Types2...> _Val2)
        : pair(_Val1, _Val2,
            index_sequence_for<_Types1...>(),
            index_sequence_for<_Types2...>())
        {   // construct from pair of tuples
        }

The above is the source code of tuple in VisualStudio. It is the piece-wise constructor of std::pair. The second constructor calls the first one. In the first constructor, data member first and second are initialized by

first(_STD get<_Indexes1>(_STD move(_Val1))...), second(_STD get<_Indexes2>(_STD move(_Val2))...)

I don't understand why std::move can be used here. What if some arguments in the tuple is not an rvalue? Wouldn't it make arguments that are lvalues to become rvalues and invoke unintended constructor of first and second?

Upvotes: 0

Views: 124

Answers (1)

Nelfeal
Nelfeal

Reputation: 13269

The first constructor is not part of the interface. It's only called for delegation by the second one, where _Val1 and _Val2 are passed by value, so the first constructor can move them.

Because of the std::move call, the overloads of std::get that can get called here are:

template< std::size_t I, class... Types >
typename std::tuple_element<I, tuple<Types...> >::type&&
get( tuple<Types...>&& t ) noexcept;

template< std::size_t I, class... Types >
typename std::tuple_element<I, tuple<Types...> >::type const&&
get( const tuple<Types...>&& t ) noexcept;

In both cases, if typename std::tuple_element<I, tuple<Types...> >::type is an lvalue reference, the return type stays an lvalue reference because T& && is just T&. So no move can occur on lvalue reference elements of the tuple.

Upvotes: 1

Related Questions