This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++17 status.

### 2485. `get()` should be overloaded for `const tuple&&`

Section: 20.5.7 [tuple.elem] Status: C++17 Submitter: Stephan T. Lavavej Opened: 2015-03-27 Last modified: 2017-07-30

Priority: 1

View all other issues in [tuple.elem].

View all issues with C++17 status.

Discussion:

`const` rvalues are weird, but they're part of the type system. Consider the following code:

>     #include <functional>
>     #include <string>
>     #include <tuple>

>     using namespace std;

>     string str1() { return "one"; }
>     const string str2() { return "two"; }
>     tuple<string> tup3() { return make_tuple("three"); }
>     const tuple<string> tup4() { return make_tuple("four"); }

>     int main() {
>       // cref(str1()); // BAD, properly rejected
>       // cref(str2()); // BAD, properly rejected
>       // cref(get<0>(tup3())); // BAD, properly rejected
>       cref(get<0>(tup4())); // BAD, but improperly accepted!
>     }

As `tuple` is a fundamental building block (and the only convenient way to have variadic data members), it should not open a hole in the type system. `get()` should imitate 7.6.1.5 [expr.ref]'s rules for accessing data members. (This is especially true for `pair`, where both `get<0>()` and `.first` are available.)

While we're in the neighborhood, we can dramatically simplify the wording here. All we need to do is follow 20.5.7 [tuple.elem]/9's example of saying "Returns: A reference to STUFF", and allow implementers to figure out how to achieve that with the given return types.

[2015-05, Lenexa]

TP: for the existing overloads there's no change to the code, just descriptions?
STL: right.
JW: I love it
MC: in favor of moving to Ready and bringing up for vote on Friday
7 in favor, none opposed

Proposed resolution:

This wording is relative to N4296.

1. Change 20.2 [utility]/2 "Header `<utility>` synopsis" as depicted:

   >     […]
   >     template<size_t I, class T1, class T2>
   >       constexpr tuple_element_t<I, pair<T1, T2>>&
   >         get(pair<T1, T2>&) noexcept;
   >     template<size_t I, class T1, class T2>
   >       constexpr tuple_element_t<I, pair<T1, T2>>&&
   >         get(pair<T1, T2>&&) noexcept;
   >     template<size_t I, class T1, class T2>
   >       constexpr const tuple_element_t<I, pair<T1, T2>>&
   >         get(const pair<T1, T2>&) noexcept;
   >     template<size_t I, class T1, class T2>
   >       constexpr const tuple_element_t<I, pair<T1, T2>>&&
   >         get(const pair<T1, T2>&&) noexcept;
   >     template <class T, class U>
   >       constexpr T& get(pair<T, U>& p) noexcept;
   >     template <class T, class U>
   >       constexpr const T& get(const pair<T, U>& p) noexcept;
   >     template <class T, class U>
   >       constexpr T&& get(pair<T, U>&& p) noexcept;
   >     template <class T, class U>
   >       constexpr const T&& get(const pair<T, U>&& p) noexcept;
   >     template <class T, class U>
   >       constexpr T& get(pair<U, T>& p) noexcept;
   >     template <class T, class U>
   >       constexpr const T& get(const pair<U, T>& p) noexcept;
   >     template <class T, class U>
   >       constexpr T&& get(pair<U, T>&& p) noexcept;
   >     template <class T, class U>
   >       constexpr const T&& get(const pair<U, T>&& p) noexcept;
   >     […]

2. Change 20.5.1 [tuple.general]/2 "Header `<tuple>` synopsis" as depicted:

   >     […]
   >     // 20.4.2.6, element access:
   >     template <size_t I, class... Types>
   >     constexpr tuple_element_t<I, tuple<Types...>>&
   >     get(tuple<Types...>&) noexcept;
   >     template <size_t I, class... Types>
   >     constexpr tuple_element_t<I, tuple<Types...>>&&
   >     get(tuple<Types...>&&) noexcept;
   >     template <size_t I, class... Types>
   >     constexpr const tuple_element_t<I, tuple<Types...>>&
   >     get(const tuple<Types...>&) noexcept;
   >     template <size_t I, class... Types>
   >     constexpr const tuple_element_t<I, tuple<Types...>>&&
   >     get(const tuple<Types...>&&) noexcept;
   >     template <class T, class... Types>
   >     constexpr T& get(tuple<Types...>& t) noexcept;
   >     template <class T, class... Types>
   >     constexpr T&& get(tuple<Types...>&& t) noexcept;
   >     template <class T, class... Types>
   >     constexpr const T& get(const tuple<Types...>& t) noexcept;
   >     template <class T, class... Types>
   >     constexpr const T&& get(const tuple<Types...>&& t) noexcept;
   >     […]

3. Change 22.3.1 [sequences.general]/2 "Header `<array>` synopsis" as depicted:

   >     […]
   >     template <size_t I, class T, size_t N>
   >     constexpr T& get(array<T, N>&) noexcept;
   >     template <size_t I, class T, size_t N>
   >     constexpr T&& get(array<T, N>&&) noexcept;
   >     template <size_t I, class T, size_t N>
   >     constexpr const T& get(const array<T, N>&) noexcept;
   >     template <size_t I, class T, size_t N>
   >     constexpr const T&& get(const array<T, N>&&) noexcept;
   >     […]

4. Change 20.4.4 [pair.astuple] as depicted:

   >     template<size_t I, class T1, class T2>
   >     constexpr tuple_element_t<I, pair<T1, T2>>&
   >     get(pair<T1, T2>& p) noexcept;
   >     template<size_t I, class T1, class T2>
   >     constexpr const tuple_element_t<I, pair<T1, T2>>&
   >     get(const pair<T1, T2>& p) noexcept;

   > > -3- Returns: If `I == 0` returns `p.first`; if `I == 1` returns `p.second`; otherwise the program is ill-formed.

   >     template<size_t I, class T1, class T2>
   >     constexpr tuple_element_t<I, pair<T1, T2>>&&
   >     get(pair<T1, T2>&& p) noexcept;
   >     template<size_t I, class T1, class T2>
   >     constexpr const tuple_element_t<I, pair<T1, T2>>&&
   >     get(const pair<T1, T2>&& p) noexcept;

   > > -4- Returns: If `I == 0` returns a reference to `std::forward<T1&&>(p.first)`; if `I == 1` returns a reference to `std::forward<T2&&>(p.second)`; otherwise the program is ill-formed.

   >     template <class T, class U>
   >     constexpr T& get(pair<T, U>& p) noexcept;
   >     template <class T, class U>
   >     constexpr const T& get(const pair<T, U>& p) noexcept;

   > > -5- Requires: `T` and `U` are distinct types. Otherwise, the program is ill-formed.

   > > -6- Returns: `get<0>(p)`;

   >     template <class T, class U>
   >     constexpr T&& get(pair<T, U>&& p) noexcept;
   >     template <class T, class U>
   >     constexpr const T&& get(const pair<T, U>&& p) noexcept;

   > > -7- Requires: `T` and `U` are distinct types. Otherwise, the program is ill-formed.

   > > -8- Returns: A reference to `p.first`.`get<0>(std::move(p))`;

   >     template <class T, class U>
   >     constexpr T& get(pair<U, T>& p) noexcept;
   >     template <class T, class U>
   >     constexpr const T& get(const pair<U, T>& p) noexcept;

   > > -9- Requires: `T` and `U` are distinct types. Otherwise, the program is ill-formed.

   > > -10- Returns: `get<1>(p)`;

   >     template <class T, class U>
   >     constexpr T&& get(pair<U, T>&& p) noexcept;
   >     template <class T, class U>
   >     constexpr const T&& get(const pair<U, T>&& p) noexcept;

   > > -11- Requires: `T` and `U` are distinct types. Otherwise, the program is ill-formed.

   > > -12- Returns: A reference to `p.second`.`get<1>(std::move(p))`;

5. Change 20.5.7 [tuple.elem] as depicted:

   >     template <size_t I, class... Types>
   >       constexpr tuple_element_t<I, tuple<Types...> >& get(tuple<Types...>& t) noexcept;

   > > -1- Requires: `I < sizeof...(Types)`. The program is ill-formed if `I` is out of bounds.

   > > -2- Returns: A reference to the `I`th element of `t`, where indexing is zero-based.

   >     template <size_t I, class... Types>
   >       constexpr tuple_element_t<I, tuple<Types...> >&& get(tuple<Types...>&& t) noexcept; // Note A

   > > -3- Effects: Equivalent to return std::forward<typename tuple_element<I, tuple<Types...> >::type&&>(get<I>(t));

   > > -4- Note: if a `T` in `Types` is some reference type `X&`, the return type is `X&`, not `X&&`. However, if the element type is a non-reference type `T`, the return type is `T&&`.

   >     template <size_t I, class... Types>
   >       constexpr tuple_element_t<I, tuple<Types...> > const& get(const tuple<Types...>& t) noexcept; // Note B
   >     template <size_t I, class... Types>
   >       constexpr const tuple_element_t<I, tuple<Types...> >&& get(const tuple<Types...>&& t) noexcept;

   > > -5- Requires: `I < sizeof...(Types)`. The program is ill-formed if `I` is out of bounds.

   > > -6- Returns: A const reference to the `I`th element of `t`, where indexing is zero-based.

   > > -?- [Note A: if a `T` in `Types` is some reference type `X&`, the return type is `X&`, not `X&&`. However, if the element type is a non-reference type `T`, the return type is `T&&`. — end note]

   > > -7- [Note B: Constness is shallow. If a `T` in `Types` is some reference type `X&`, the return type is `X&`, not `const X&`. However, if the element type is non-reference type `T`, the return type is `const T&`. This is consistent with how constness is defined to work for member variables of reference type. — end note]

   >     template <class T, class... Types>
   >       constexpr T& get(tuple<Types...>& t) noexcept;
   >     template <class T, class... Types>
   >       constexpr T&& get(tuple<Types...>&& t) noexcept;
   >     template <class T, class... Types>
   >       constexpr const T& get(const tuple<Types...>& t) noexcept;
   >     template <class T, class... Types>
   >       constexpr const T&& get(const tuple<Types...>&& t) noexcept;

   > > -8- Requires: The type `T` occurs exactly once in `Types...`. Otherwise, the program is ill-formed.

   > > -9- Returns: A reference to the element of `t` corresponding to the type `T` in `Types...`.

   > > […]

6. Change 22.3.7.7 [array.tuple] as depicted:

   >     template <size_t I, class T, size_t N>
   >       constexpr T& get(array<T, N>& a) noexcept;

   > > -3- Requires: `I < N`. The program is ill-formed if `I` is out of bounds.

   > > -4- Returns: A reference to the `I`th element of `a`, where indexing is zero-based.

   >     template <size_t I, class T, size_t N>
   >       constexpr T&& get(array<T, N>&& a) noexcept;

   > > -5- Effects: Equivalent to `return std::move(get<I>(a));`

   >     template <size_t I, class T, size_t N>
   >       constexpr const T& get(const array<T, N>& a) noexcept;
   >     template <size_t I, class T, size_t N>
   >       constexpr const T&& get(const array<T, N>&& a) noexcept;

   > > -6- Requires: `I < N`. The program is ill-formed if `I` is out of bounds.

   > > -7- Returns: A const reference to the `I`th element of `a`, where indexing is zero-based.