8 General utilities library [utilities]

8.5 Tagged tuple-like types [taggedtup]

8.5.1 General [taggedtup.general]

The library provides a template for augmenting a tuple-like type with named element accessor member functions. The library also provides several templates that provide access to tagged objects as if they were tuple objects (see ISO/IEC 14882:2014 §[tuple.elem]).

8.5.2 Class template tagged [taggedtup.tagged]

Class template tagged augments a tuple-like class type (e.g., pair ( ISO/IEC 14882:2014 §[pairs]), tuple ( ISO/IEC 14882:2014 §[tuple])) by giving it named accessors. It is used to define the alias templates tagged_pair ([tagged.pairs]) and tagged_tuple ([tagged.tuple]).

In the class synopsis below, let i be in the range [0,sizeof...(Tags)) and Ti be the ith type in Tags, where indexing is zero-based.

// defined in header <experimental/ranges/utility>

namespace std { namespace experimental { namespace ranges { inline namespace v1 {
  template <class T>
  concept bool TagSpecifier = implementation-defined;

  template <class F>
  concept bool TaggedType = implementation-defined;

  template <class Base, TagSpecifier... Tags>
    requires sizeof...(Tags) <= tuple_size<Base>::value
  struct tagged :
    Base, TAGGET(tagged<Base, Tags...>, Ti, i)... { // see below
    using Base::Base;
    tagged() = default;
    tagged(tagged&&) = default;
    tagged(const tagged&) = default;
    tagged &operator=(tagged&&) = default;
    tagged &operator=(const tagged&) = default;
    tagged(Base&&) noexcept(see below)
      requires MoveConstructible<Base>;
    tagged(const Base&) noexcept(see below)
      requires CopyConstructible<Base>;
    template <class Other>
      requires Constructible<Base, Other>
    constexpr tagged(tagged<Other, Tags...> &&that) noexcept(see below);
    template <class Other>
      requires Constructible<Base, const Other&>
    constexpr tagged(const tagged<Other, Tags...> &that);
    template <class Other>
      requires Assignable<Base&, Other>
    constexpr tagged& operator=(tagged<Other, Tags...>&& that) noexcept(see below);
    template <class Other>
      requires Assignable<Base&, const Other&>
    constexpr tagged& operator=(const tagged<Other, Tags...>& that);
    template <class U>
      requires Assignable<Base&, U> && !Same<decay_t<U>, tagged>
    constexpr tagged& operator=(U&& u) noexcept(see below);
    constexpr void swap(tagged& that) noexcept(see below)
      requires Swappable<Base>;
    friend constexpr void swap(tagged&, tagged&) noexcept(see below)
      requires Swappable<Base>;
  };
}}}}

A tagged getter is an empty trivial class type that has a named member function that returns a reference to a member of a tuple-like object that is assumed to be derived from the getter class. The tuple-like type of a tagged getter is called its DerivedCharacteristic. The index of the tuple element returned from the getter's member functions is called its ElementIndex. The name of the getter's member function is called its ElementName

A tagged getter class with DerivedCharacteristic D, ElementIndex N, and ElementName name shall provide the following interface:

struct __TAGGED_GETTER {
  constexpr decltype(auto) name() &       { return get<N>(static_cast<D&>(*this)); }
  constexpr decltype(auto) name() &&      { return get<N>(static_cast<D&&>(*this)); }
  constexpr decltype(auto) name() const & { return get<N>(static_cast<const D&>(*this)); }
};

A tag specifier is a type that facilitates a mapping from a tuple-like type and an element index into a tagged getter that gives named access to the element at that index. TagSpecifier<T> is satisfied if and only if T is a tag specifier. The tag specifiers in the Tags parameter pack shall be unique. [ Note: The mapping mechanism from tag specifier to tagged getter is unspecified. — end note ]

Let TAGGET(D, T, N) name a tagged getter type that gives named access to the N-th element of the tuple-like type D.

It shall not be possible to delete an instance of class template tagged through a pointer to any base other than Base.

TaggedType<F> is satisfied if and only if F is a unary function type with return type T which satisfies TagSpecifier<T>. Let TAGSPEC(F) name the tag specifier of the TaggedType F, and let TAGELEM(F) name the argument type of the TaggedType F.

tagged(Base&& that) noexcept(see below) requires MoveConstructible<Base>;

Effects: Initializes Base with std::move(that).

Remarks: The expression in the noexcept is equivalent to:

is_nothrow_move_constructible<Base>::value

tagged(const Base& that) noexcept(see below) requires CopyConstructible<Base>;

Effects: Initializes Base with that.

Remarks: The expression in the noexcept is equivalent to:

is_nothrow_copy_constructible<Base>::value

template <class Other> requires Constructible<Base, Other> constexpr tagged(tagged<Other, Tags...> &&that) noexcept(see below);

Effects: Initializes Base with static_cast<Other&&>(that).

Remarks: The expression in the noexcept is equivalent to:

is_nothrow_constructible<Base, Other>::value

template <class Other> requires Constructible<Base, const Other&> constexpr tagged(const tagged<Other, Tags...>& that);

Effects: Initializes Base with static_cast<const Other&>(that).

template <class Other> requires Assignable<Base&, Other> constexpr tagged& operator=(tagged<Other, Tags...>&& that) noexcept(see below);

Effects: Assigns static_cast<Other&&>(that) to static_cast<Base&>(*this).

Returns: *this.

Remarks: The expression in the noexcept is equivalent to:

is_nothrow_assignable<Base&, Other>::value

template <class Other> requires Assignable<Base&, const Other&> constexpr tagged& operator=(const tagged<Other, Tags...>& that);

Effects: Assigns static_cast<const Other&>(that) to static_cast<Base&>(*this).

Returns: *this.

template <class U> requires Assignable<Base&, U> && !Same<decay_t<U>, tagged> constexpr tagged& operator=(U&& u) noexcept(see below);

Effects: Assigns std::forward<U>(u) to static_cast<Base&>(*this).

Returns: *this.

Remarks: The expression in the noexcept is equivalent to:

is_nothrow_assignable<Base&, U>::value

constexpr void swap(tagged& rhs) noexcept(see below) requires Swappable<Base>;

Effects: Calls swap on the result of applying static_cast to *this and that.

Throws: Nothing unless the call to swap on the Base sub-objects throws.

Remarks: The expression in the noexcept is equivalent to:

noexcept(swap(declval<Base&>(), declval<Base&>()))

friend constexpr void swap(tagged& lhs, tagged& rhs) noexcept(see below) requires Swappable<Base>;

Effects: Equivalent to lhs.swap(rhs).

Remarks: The expression in the noexcept is equivalent to:

noexcept(lhs.swap(rhs))

8.5.3 Tuple-like access to tagged [tagged.astuple]

namespace std { template <class Base, class... Tags> struct tuple_size<experimental::ranges::tagged<Base, Tags...>> : tuple_size<Base> { }; template <size_t N, class Base, class... Tags> struct tuple_element<N, experimental::ranges::tagged<Base, Tags...>> : tuple_element<N, Base> { }; }

8.5.4 Alias template tagged_pair [tagged.pairs]

// defined in header <experimental/ranges/utility>

namespace std { namespace experimental { namespace ranges { inline namespace v1 {
  // ...
  template <TaggedType T1, TaggedType T2>
  using tagged_pair = tagged<pair<TAGELEM(T1), TAGELEM(T2)>,
                             TAGSPEC(T1), TAGSPEC(T2)>;
}}}}

Example:

// See [alg.tagspec]:
tagged_pair<tag::min(int), tag::max(int)> p{0, 1};
assert(&p.min() == &p.first);
assert(&p.max() == &p.second);

 — end example ]

8.5.4.1 Tagged pair creation functions [tagged.pairs.creation]

// defined in header <experimental/ranges/utility> namespace std { namespace experimental { namespace ranges { inline namespace v1 { template <TagSpecifier Tag1, TagSpecifier Tag2, class T1, class T2> constexpr see below make_tagged_pair(T1&& x, T2&& y); }}}}

Let P be the type of make_pair(std::forward<T1>(x), std::forward<T2>(y)). Then the return type is tagged<P, Tag1, Tag2>.

Returns: {std::forward<T1>(x), std::forward<T2>(y)}.

Example: In place of:

  return tagged_pair<tag::min(int), tag::max(double)>(5, 3.1415926);   // explicit types

a C++ program may contain:

  return make_tagged_pair<tag::min, tag::max>(5, 3.1415926);           // types are deduced

 — end example ]

8.5.5 Alias template tagged_tuple [tagged.tuple]

Header <experimental/ranges/tuple> synopsis

namespace std { namespace experimental { namespace ranges { inline namespace v1 {
  template <TaggedType... Types>
  using tagged_tuple = tagged<tuple<TAGELEM(Types)...>,
                              TAGSPEC(Types)...>;

  template <TagSpecifier... Tags, class... Types>
    requires sizeof...(Tags) == sizeof...(Types)
      constexpr see below make_tagged_tuple(Types&&... t);
}}}}
template <TaggedType... Types>
using tagged_tuple = tagged<tuple<TAGELEM(Types)...>,
                            TAGSPEC(Types)...>;

Example:

// See [alg.tagspec]:
tagged_tuple<tag::in(char*), tag::out(char*)> t{0, 0};
assert(&t.in() == &get<0>(t));
assert(&t.out() == &get<1>(t));

 — end example ]

8.5.5.1 Tagged tuple creation functions [tagged.tuple.creation]

template <TagSpecifier... Tags, class... Types> requires sizeof...(Tags) == sizeof...(Types) constexpr see below make_tagged_tuple(Types&&... t);

Let T be the type of make_tuple(std::forward<Types>(t)...). Then the return type is tagged<T, Tags...>.

Returns: tagged<T, Tags...>(std::forward<Types>(t)...).

Example:

int i; float j;
make_tagged_tuple<tag::in1, tag::in2, tag::out>(1, ref(i), cref(j))

creates a tagged tuple of type

tagged_tuple<tag::in1(int), tag::in2(int&), tag::out(const float&)>

 — end example ]