std::unorde...nordered_map(3)

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

🌸 std::unordered_map::unordered_map (3) β€” C++ Standard Libary 🌸

🏷️ NAME 🏷️

std::unordered_map::unordered_map β€” std::unordered_map::unordered_map

Okay bestie, yes the NAME section is literally the same thing twice. It's giving "I have no original thoughts," but make no mistake: this is the constructor page for std::unordered_map, and it's lowkey iconic. πŸ’…

πŸ“œ Synopsis πŸ“œ

This synopsis is long and a little chaotic β€” it's giving caffeinated PDF β€” but it's just eighteen constructor overloads. Here's the code exactly as the manual page lays it out, line breaks and all, because we do not mess with signatures. πŸ”₯

          unordered_map()
       (since C++11)
              : unordered_map(size_type(/* implementation-defined */))
       (until C++20)
          {}
          unordered_map();
       (since C++20)
          explicit unordered_map( size_type bucket_count,

                                  const Hash& hash = Hash(),
                                  const key_equal& equal = key_equal(),     (2)
       (since C++11)

                                  const Allocator& alloc = Allocator()
          );
          unordered_map( size_type bucket_count,

                         const Allocator& alloc )                           (3)
       (since C++14)

              : unordered_map(bucket_count, Hash(), key_equal(), alloc)
          {}
          unordered_map( size_type bucket_count,

                         const Hash& hash,
                         const Allocator& alloc )                           (4)
       (since C++14)

              : unordered_map(bucket_count, hash, key_equal(), alloc)
          {}
          explicit unordered_map( const Allocator& alloc );                 (5)
       (since C++11)
          template< class InputIt >

          unordered_map( InputIt first, InputIt last,
                         size_type bucket_count = /*
          implementation-defined */,                                        (6)
       (since C++11)
                         const Hash& hash = Hash(),
                         const key_equal& equal = key_equal(),

                         const Allocator& alloc = Allocator() );
          template< class InputIt >

          unordered_map( InputIt first, InputIt last,
                         size_type bucket_count,
                         const Allocator& alloc )                           (7)
       (since C++14)
              : unordered_map(first, last,

                              bucket_count, Hash(), key_equal(), alloc)
          {}
          template< class InputIt >

          unordered_map( InputIt first, InputIt last,
                         size_type bucket_count,
                         const Hash& hash,                                  (8)
       (since C++14)
                         const Allocator& alloc )
              : unordered_map(first, last,

                              bucket_count, hash, key_equal(), alloc)
          {}
          unordered_map( const unordered_map& other );                      (9)
       (since C++11)
          unordered_map( const unordered_map& other, const Allocator&   (1)
       (10) (since C++11)
          alloc );
          unordered_map( unordered_map&& other );
       (11) (since C++11)
          unordered_map( unordered_map&& other, const Allocator& alloc
       (12) (since C++11)
          );
          unordered_map( std::initializer_list<value_type> init,

                         size_type bucket_count = /*
          implementation-defined */,
       (13) (since C++11)
                         const Hash& hash = Hash(),
                         const key_equal& equal = key_equal(),

                         const Allocator& alloc = Allocator() );
          unordered_map( std::initializer_list<value_type> init,

                         size_type bucket_count,
                         const Allocator& alloc )
       (14) (since C++14)
              : unordered_map(init, bucket_count,

                              Hash(), key_equal(), alloc) {}
          unordered_map( std::initializer_list<value_type> init,

                         size_type bucket_count,
                         const Hash& hash,
       (15) (since C++14)
                         const Allocator& alloc )
              : unordered_map(init, bucket_count,

                              hash, key_equal(), alloc) {}
          template< container-compatible-range<value_type> R >

          unordered_map( std::from_range_t, R&& rg,
                         size_type bucket_count = /* see description
          */,
       (16) (since C++23)
                         const Hash& hash = Hash(),
                         const key_equal& equal = key_equal(),

                         const Allocator& alloc = Allocator() );
          template< container-compatible-range<value_type> R >

          unordered_map( std::from_range_t, R&& rg,
                         size_type bucket_count,
                         const Allocator& alloc )
       (17) (since C++23)
              : unordered_map(std::from_range, std::forward<R>(rg),

                              bucket_count, Hash(), key_equal(), alloc)
          {}
          template< container-compatible-range<value_type> R >

          unordered_map( std::from_range_t, R&& rg,
                         size_type bucket_count,
                         const Hash& hash,
       (18) (since C++23)
                         const Alloc& alloc )
              : unordered_map(std::from_range, std::forward<R>(rg),

                              bucket_count, hash, key_equal(), alloc)
          {}

The numbers in parentheses are the manual's overload labels, and they're your map for the tea below. 🫢

Okay so the big picture: this constructs a new container from a variety of data sources. Optionally it uses the user-supplied bucket_count as a minimal number of buckets to create, hash as the hash function, equal as the function to compare keys, and alloc as the allocator. ✨

  • 1-5) Constructs an empty container. Sets max_load_factor() to 1.0. For the default constructor, the number of buckets is implementation-defined.
  • 6-8) Constructs the container with the contents of the range [first, last). Sets max_load_factor() to 1.0. If multiple elements in the range have keys that compare equivalent, it is unspecified which element is inserted (pending LWG2844).
  • 9,10) Copy constructor. Constructs the container with a copy of the contents of other; it also copies the load factor, the predicate, and the hash function. If alloc is not provided, the allocator is obtained by calling std::allocator_traits<allocator_type>::select_on_container_copy_construction(other.get_allocator()).
    • (since C++23) The template parameter Allocator is only deduced from the first argument while used in class template argument deduction.
  • 11,12) Move constructor. Constructs the container with the contents of other using move semantics. If alloc is not provided, the allocator is obtained by move-construction from the allocator belonging to other.
    • (since C++23) The template parameter Allocator is only deduced from the first argument while used in class template argument deduction.
  • 13-15) Initializer-list constructor. Constructs the container with the contents of the initializer list init, same as unordered_map(init.begin(), init.end()).
  • 16-18) Constructs the container with the contents of rg. If multiple elements in the range have keys that compare equivalent, it is unspecified which element is inserted (pending LWG2844).

πŸŽ€ Parameters πŸŽ€

  • alloc β€” allocator to use for all memory allocations of this container
  • bucket_count β€” minimal number of buckets to use on initialization. If it is not specified, an implementation-defined default value is used
  • hash β€” hash function to use
  • equal β€” comparison function to use for all key comparisons of this container
  • first, last β€” the range [first, last) to copy the elements from
  • rg β€” a container compatible range, that is, an input_range whose elements are convertible to value_type
  • other β€” another container to be used as source to initialize the elements of the container with
  • init β€” initializer list to initialize the elements of the container with

🧚 Type requirements 🧚

  • InputIt must meet the requirements of LegacyInputIterator.

πŸ”₯ Complexity πŸ”₯

  • 1-5) Constant.
  • 6-8) Average case linear (i.e. O(N), where N is std::distance(first, last)), worst case quadratic, i.e. O(N^2).
  • 9,10) Linear in size of other.
  • 11,12) Constant. If alloc is given and alloc != other.get_allocator(), then linear.
  • 13-15) Average case O(N) (N is std::size(init)), worst case O(N^2).
  • 16-18) Average case O(N) (N is ranges::distance(rg)), worst case O(N^2).

πŸ’€ Exceptions πŸ’€

Calls to Allocator::allocate may throw. 😭

🌈 Notes 🌈

After container move construction (overload (4)), references, pointers, and iterators (other than the end iterator) to other remain valid, but refer to elements that are now in *this. The current standard makes this guarantee via the blanket statement in [container.reqmts]/67, and a more direct guarantee is under consideration via LWG issue 2321.

Although not formally required until C++23, some implementations have already put the template parameter Allocator into non-deduced contexts in earlier modes.

πŸ“Š Feature-test macro πŸ“Š

  • __cpp_lib_containers_ranges β€” 202202L β€” (C++23) β€” Ranges-aware construction and insertion; overloads (16-18)

✨ EXAMPLE ✨

okay bestie, this example is literally the full tour β€” default constructor, list constructor, copy, move, range, custom Key with KeyHash/KeyEqual, a Foo with a std::hash specialization, and even lambdas for the custom hash/equality vibe. it's giving "one file, every constructor mood" and honestly? iconic. πŸ’…

       // Run this code

        #include <bitset>
        #include <string>
        #include <unordered_map>
        #include <utility>
        #include <vector>

        struct Key
        {
            std::string first;
            std::string second;
        };

        struct KeyHash
        {
            std::size_t operator()(const Key& k) const
            {
                return std::hash<std::string>()(k.first) ^
                    (std::hash<std::string>()(k.second) << 1);
            }
        };

        struct KeyEqual
        {
            bool operator()(const Key& lhs, const Key& rhs) const
            {
                return lhs.first == rhs.first && lhs.second == rhs.second;
            }
        };

        struct Foo
        {
            Foo(int val_) : val(val_) {}
            int val;
            bool operator==(const Foo &rhs) const { return val == rhs.val; }
        };

        template<> struct std::hash<Foo>
        {
            std::size_t operator()(const Foo &f) const
            {
                return std::hash<int>{}(f.val);
            }
        };

        int main()
        {
            // default constructor: empty map
            std::unordered_map<std::string, std::string> m1;

            // list constructor
            std::unordered_map<int, std::string> m2 =
            {
                {1, "foo"},
                {3, "bar"},
                {2, "baz"}
            };

            // copy constructor
            std::unordered_map<int, std::string> m3 = m2;

            // move constructor
            std::unordered_map<int, std::string> m4 = std::move(m2);

            // range constructor
            std::vector<std::pair<std::bitset<8>, int>> v = {{0x12, 1},
       {0x01,-1}};
            std::unordered_map<std::bitset<8>, double> m5(v.begin(), v.end());

            // Option 1 for a constructor with a custom Key type
            // Define the KeyHash and KeyEqual structs and use them in the
       template
            std::unordered_map<Key, std::string, KeyHash, KeyEqual> m6 =
            {
                {{"John", "Doe"}, "example"},
                {{"Mary", "Sue"}, "another"}
            };

            // Option 2 for a constructor with a custom Key type.
            // Define a const == operator for the class/struct and specialize
       std::hash
            // structure in the std namespace
            std::unordered_map<Foo, std::string> m7 =
            {
                {Foo(1), "One"}, {2, "Two"}, {3, "Three"}
            };

            // Option 3: Use lambdas
            // Note that the initial bucket count has to be passed to the
       constructor
            struct Goo { int val; };
            auto hash = [](const Goo &g){ return std::hash<int>{}(g.val); };
            auto comp = [](const Goo &l, const Goo &r){ return l.val == r.val;
       };
            std::unordered_map<Goo, double, decltype(hash), decltype(comp)>
       m8(10, hash, comp);
        }

🩹 DEFECT REPORTS 🩹

so these are the behavior-changing defect reports that got applied retroactively to previously published C++ standards β€” yes, the committee really said "actually, bestie, let's circle back and fix that" after the fact. no cap. 🫢

  • LWG 2193 β€” applied to C++11: behavior as published was that the default constructor (1) was explicit; correct behavior is that it's non-explicit. slay for implicit construction, I guess πŸ’…

πŸ‘€ SEE ALSO πŸ‘€

  • operator= β€” assigns values to the container (public member function)

that's the whole page, babe β€” source is http://cppreference.com, version/date is 2024.06.10, and the page is std::unorde...nordered_map(3) πŸ¦‹πŸ’– mwah, go build something cute with your buckets. ✨

πŸ“¦ where this page comes from