std::pair::pair(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– std::pair::pair(3) β€” C++ Standard Library πŸ’–

✨ NAME ✨

std::pair::pair β€” std::pair::pair 🫢 (yes bestie, the name is literally the name, we love a self-referential icon πŸ’…)

🌈 Synopsis 🌈

okay so here's the whole constructor lineup, and it's giving many overloads, very main character πŸ‘‘ the version tags are doing a lot, but every single one is here, no cap.

pair();                                            // (1) (until C++11)
constexpr pair();                                  // (1) (since C++11)

(conditionally explicit) pair( const T1& x, const T2& y ); // (2) (until C++11)
(conditionally explicit) pair( const T1& x, const T2& y ); // (2) (since C++11) (until C++14)
(conditionally explicit) constexpr pair( const T1& x, const T2& y ); // (2) (since C++14)

template< class U1, class U2 >
(conditionally explicit) pair( U1&& x, U2&& y ); // (3) (since C++11) (until C++14)
template< class U1, class U2 >
(conditionally explicit) constexpr pair( U1&& x, U2&& y ); // (3) (since C++14) (until C++23)
template< class U1 = T1, class U2 = T2 >
(conditionally explicit) constexpr pair( U1&& x, U2&& y ); // (3) (since C++23)

template< class U1, class U2 >
(conditionally explicit) pair( pair<U1, U2>& p ); // (4) (until C++11)
template< class U1, class U2 >
(conditionally explicit) constexpr pair( pair<U1, U2>& p ); // (4) (since C++23)

template< class U1, class U2 >
(conditionally explicit) pair( const pair<U1, U2>& p ); // (5) (since C++11) (until C++14)
template< class U1, class U2 >
(conditionally explicit) constexpr pair( const pair<U1, U2>& p ); // (5) (since C++14)

template< class U1, class U2 >
(conditionally explicit) pair( pair<U1, U2>&& p ); // (6) (since C++11) (until C++14)
template< class U1, class U2 >
(conditionally explicit) constexpr pair( pair<U1, U2>&& p ); // (6) (since C++14)

template< class U1, class U2 >
(conditionally explicit) constexpr pair( const pair<U1, U2>&& p ); // (7) (since C++23)

template< pair-like P >
(conditionally explicit) constexpr pair ( P&& u ); // (8) (since C++23)

template< class... Args1, class... Args2 >
pair( std::piecewise_construct_t, std::tuple<Args1...> first_args,
      std::tuple<Args2...> second_args ); // (9) (since C++11) (until C++20)
template< class... Args1, class... Args2 >
constexpr pair( std::piecewise_construct_t, std::tuple<Args1...> first_args,
                std::tuple<Args2...> second_args ); // (9) (since C++20)

pair( const pair& p ) = default; // (10)
pair( pair&& p ) = default; // (11) (since C++11)
bestie the original synopsis also tagged the pre-C++20 piecewise one as (6) while the prose below numbers the rvalue-pair one as (6) β€” the columns were in their flop era πŸ’€ but every crumb is preserved, iykyk.

πŸ’… The constructor lore, 1 through 11 πŸ’…

  • 1) Default constructor. Value-initializes both elements of the pair, first and second.

This constructor participates in overload resolution if and only if std::is_default_constructible_v<T1> and std::is_default_constructible_v<T2> are both true. (since C++11)

This constructor is explicit if and only if either T1 or T2 is not implicitly default-constructible.

  • 2) Initializes first with x and second with y.

This constructor participates in overload resolution if and only if std::is_copy_constructible_v<T1> and std::is_copy_constructible_v<T2> are both true. (since C++11)

This constructor is explicit if and only if std::is_convertible_v<const T1&, T1> is false or std::is_convertible_v<const T2&, T2> is false.

  • 3) Initializes first with std::forward<U1>(x) and second with std::forward<U2>(y).

This constructor participates in overload resolution if and only if std::is_constructible_v<T1, U1> and std::is_constructible_v<T2, U2> are both true.

This constructor is explicit if and only if std::is_convertible_v<U1, T1> is false or std::is_convertible_v<U2, T2> is false.

This constructor is defined as deleted if the initialization of first or second would bind a reference to temporary object. (since C++23)

  • 4) Initializes first with p.first and second with p.second.

This constructor participates in overload resolution if and only if std::is_constructible_v<T1, U1&> and std::is_constructible_v<T2, U2&> are both true.

This constructor is explicit if and only if std::is_convertible_v<U1&, T1> is false or std::is_convertible_v<U2&, T2> is false.

This constructor is defined as deleted if the initialization of first or second would bind a reference to temporary object. (since C++23)

  • 5) Initializes first with p.first and second with p.second.

This constructor participates in overload resolution if and only if std::is_constructible_v<T1, const U1&> and std::is_constructible_v<T2, const U2&> are both true. (since C++11)

This constructor is explicit if and only if std::is_convertible_v<const U1&, T1> is false or std::is_convertible_v<const U2&, T2> is false.

This constructor is defined as deleted if the initialization of first or second would bind a reference to temporary object. (since C++23)

  • 6) Initializes first with std::forward<U1>(p.first) and second with std::forward<U2>(p.second).

This constructor participates in overload resolution if and only if std::is_constructible_v<T1, U1> and std::is_constructible_v<T2, U2> are both true.

This constructor is explicit if and only if std::is_convertible_v<U1, T1> is false or std::is_convertible_v<U2, T2> is false.

This constructor is defined as deleted if the initialization of first or second would bind a reference to temporary object. (since C++23)

  • 7) Initializes first with std::forward<const U1>(p.first) and second with std::forward<const U2>(p.second).

This constructor participates in overload resolution if and only if std::is_constructible_v<T1, U1> and std::is_constructible_v<T2, U2> are both true.

This constructor is explicit if and only if std::is_convertible_v<const U1, T1> is false or std::is_convertible_v<const U2, T2> is false.

This constructor is defined as deleted if the initialization of first or second would bind a reference to temporary object.

  • 8) Given u1 as std::get<0>(std::forward(u)) and u2 as std::get<1>(std::forward(u)), denote their types as U1 and U2 respectively. Initializes first with u1 and second with u2.

This constructor participates in overload resolution if and only if:

  • std::remove_cvref(P) is not a specialization of std::ranges::subrange,
  • std::is_constructible_v<T1, U1> is true, and
  • std::is_constructible_v<T2, U2> is true.
tiny original typo alert: the page had std::is_constructible_v<T2, U2 is true. β€” bestie, the missing > is giving chaos, but the meaning is clear 🫢

This constructor is explicit if and only if std::is_convertible_v<U1, T1> is false or std::is_convertible_v<U2, T2> is false.

This constructor is defined as deleted if the initialization of first or second would bind a reference to temporary object. (since C++23)

  • 9) Forwards the elements of first_args to the constructor of first and forwards the elements of second_args to the constructor of second. This is the only non-default constructor that can be used to create a pair of non-copyable non-movable types. The program is ill-formed if first or second is a reference and bound to a temporary object.
  • 10) Copy constructor is implicitly declared (until C++11), defaulted, and is constexpr if copying of both elements satisfies the requirements on constexpr functions (since C++11).
  • 11) Move constructor is defaulted, and is constexpr if moving of both elements satisfies the requirements on constexpr functions. (since C++11)

🌸 Parameters 🌸

  • x β€” value to initialize the first element of this pair
  • y β€” value to initialize the second element of this pair
  • p β€” pair of values used to initialize both elements of this pair
  • u β€” pair-like object of values used to initialize both elements of this pair
  • first_args β€” tuple of constructor arguments to initialize the first element of this pair
  • second_args β€” tuple of constructor arguments to initialize the second element of this pair

πŸ’€ Exceptions πŸ’€

Does not throw exceptions unless one of the specified operations (e.g. constructor of an element) throws. 🫢

πŸŽ€ Example πŸŽ€

run this, it's iconic ✨

// Run this code

#include <complex>
#include <iostream>
#include <string>
#include <tuple>
#include <utility>

int main()
{
    auto print = [](auto rem, auto const& pair)
    {
        std::cout << rem << "(" << pair.first << ", " << pair.second << ")\n";
    };

    std::pair<int, float> p1;
    print("(1) Value-initialized: ", p1);

    std::pair<int, double> p2{42, 3.1415};
    print("(2) Initialized with two values: ", p2);

    std::pair<char, int> p4{p2};
    print("(4) Implicitly converted: ", p4);

    std::pair<std::complex<double>, std::string> p6
        {std::piecewise_construct, std::forward_as_tuple(0.123, 7.7),
            std::forward_as_tuple(10, 'a')};
    print("(8) Piecewise constructed: ", p6);
}

okay bestie, that's part 1 β€” part 2 is pulling up, don't go anywhere πŸ’…πŸ¦‹

Possible output β€” okay so when that little example runs, this is the exact vibe it’s giving:

(1) Value-initialized: (0, 0)
(2) Initialized with two values: (42, 3.1415)
(4) Implicitly converted: (*, 3)
(8) Piecewise constructed: ((0.123,7.7), aaaaaaaaaa)

No cap, that (*, 3) line is doing its own thing and we respect it. 🫢


🩹 Defect reports 🩹

Bestie, the following behavior-changing defect reports were applied retroactively to previously published C++ standards β€” so the lore got patched after the fact:

  • DR LWG 265 β€” applied to C++98. Behavior as published: the default constructor copy-initialized first and second with T1() and T2() respectively (which meant it required T1 and T2 to be CopyConstructible). Correct behavior: first and second are value-initialized. πŸ’…
  • DR LWG 2510 β€” applied to C++11. Behavior as published: the default constructor was implicit. Correct behavior: made conditionally-explicit. ✨
  • DR N4387 β€” applied to C++11. Behavior as published: some constructors were implicit-only, preventing some uses. Correct behavior: constructors made conditionally-explicit. πŸ”₯

πŸ‘€ See also πŸ‘€

  • make_pair β€” creates a pair object of type, defined by the argument types. (function template)
  • constructor β€” constructs a new tuple. (public member function of std::tuple<Types...>)

πŸŽ€ source: cppreference.com Β· version/date: 2024.06.10 Β· page: std::pair::pair(3) Β· ty for reading bestie, periodt 🫢

πŸ“¦ where this page comes from