Changeset 871cfe0c in mainline


Ignore:
Timestamp:
2018-07-05T21:41:21Z (6 years ago)
Author:
Dzejrou <dzejrou@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
e9027b5
Parents:
e29ce3d
git-author:
Dzejrou <dzejrou@…> (2018-04-23 18:12:24)
git-committer:
Dzejrou <dzejrou@…> (2018-07-05 21:41:21)
Message:

cpp: added insertion, iterators and some misc operations to aux::hash_table

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/cpp/include/internal/hash_table.hpp

    re29ce3d r871cfe0c  
    3232#include <cstdlib>
    3333#include <internal/list.hpp>
     34#include <iterator>
    3435#include <memory>
     36#include <tuple>
    3537#include <utility>
    3638
     
    4850     */
    4951
    50     template<class Key, Value>
     52    template<class Key, class Value>
    5153    struct key_value_key_extractor
    5254    {
     
    6870    struct key_value_allocator
    6971    { /* DUMMY BODY */ };
    70 
    71     template<>
    72     struct allocator_traits<key_value_allocator>
    73     {
    74         template<class Alloc, class Key, class Value, class... Args>
    75         static void construct(Alloc& alloc, pair<Key, Value>* ptr, Args&&... args)
    76         {
    77             alloc.construct(&ptr->second, forward<Args>(args)...);
    78         }
    79     };
    80 
    81     struct hash_single_policy
    82     {
    83         // TODO: umap/uset operations
    84     };
    85 
    86     struct hash_multi_policy
    87     {
    88         // TODO: umultimap/umultiset operations
    89     };
    9072
    9173    template<class Value, class Size>
     
    9880         */
    9981
    100         Size count;
    101         link_node<Value>* head;
     82        list_node<Value>* head;
     83
     84        Size size() const noexcept
     85        {
     86            // TODO: implement
     87        }
     88
     89        void append(list_node<Value>* node)
     90        {
     91            if (!head)
     92                head = node;
     93            else
     94                head->append(node);
     95        }
    10296
    10397        ~hash_table_bucket()
     
    107101    };
    108102
    109     // TODO: iterator, const iterator, local iterator, const local iterator
    110     //       and also possibly two versions of each for umap and uset
     103    struct hash_single_policy
     104    {
     105        // TODO: umap/uset operations
     106
     107        template<class Table, class Key>
     108        static typename Table::size_type count(const Table& table, const Key& key)
     109        {
     110            return table.find(key) == table.end() ? 0 : 1;
     111        }
     112
     113        template<class Table, class Key>
     114        static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key)
     115        {
     116            auto idx = table.get_bucket_idx_(key);
     117            return make_tuple(
     118                &table.table_[idx],
     119                table.table_[idx].head,
     120                idx
     121            );
     122        }
     123    };
     124
     125    struct hash_multi_policy
     126    {
     127        // TODO: umultimap/umultiset operations
     128
     129        template<class Table, class Key>
     130        static typename Table::size_type count(const Table& table, const Key& key)
     131        {
     132            auto head = table.table_[get_bucket_idx_(key)].head;
     133            if (!head)
     134                return 0;
     135
     136            auto current = head;
     137            typename Table::size_type res = 0;
     138            do
     139            {
     140                if (table.key_eq_(key, table.key_extractor_(current->value)))
     141                    ++res;
     142
     143                current = current->next;
     144            }
     145            while (current != head);
     146
     147            return res;
     148        }
     149
     150        template<class Table, class Key>
     151        static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key)
     152        {
     153            // TODO: find the right bucket, in that, find the right
     154            //       link (if key is already in it, place the new copy
     155            //       next to it, otherwise just return head)
     156        }
     157    };
     158
     159    template<class Value, class ConstReference, class ConstPointer, class Size>
     160    class hash_table_const_iterator
     161    {
     162        public:
     163            using value_type      = Value;
     164            using size_type       = Size;
     165            using const_reference = ConstReference;
     166            using const_pointer   = ConstPointer;
     167            using difference_type = ptrdiff_t;
     168
     169            using iterator_category = forward_iterator_tag;
     170
     171            hash_table_const_iterator(hash_table_bucket<value_type, size_type>* table = nullptr,
     172                                      size_type idx = size_type{}, size_type max_idx = size_type{},
     173                                      list_node<value_type>* current = nullptr)
     174                : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
     175            { /* DUMMY BODY */ }
     176
     177            hash_table_const_iterator(const hash_table_const_iterator&) = default;
     178            hash_table_const_iterator& operator=(const hash_table_const_iterator&) = default;
     179
     180            const_reference operator*() const
     181            {
     182                return current_->value;
     183            }
     184
     185            const_pointer operator->() const
     186            {
     187                return &current_->value;
     188            }
     189
     190            hash_table_const_iterator& operator++()
     191            {
     192                current_ = current_->next;
     193                if (current_ == table_[idx_].head)
     194                {
     195                    if (idx_ < max_idx_)
     196                    {
     197                        while (!table_[++idx_].head)
     198                        { /* DUMMY BODY */ }
     199
     200                        if (idx_ < max_idx_)
     201                            current_ = table_[idx_].head;
     202                        else
     203                            current_ = nullptr;
     204                    }
     205                    else
     206                        current_ = nullptr;
     207                }
     208
     209                return *this;
     210            }
     211
     212            hash_table_const_iterator operator++(int)
     213            {
     214                auto tmp_current = current_;
     215                auto tmp_idx = idx_;
     216
     217                current_ = current_->next;
     218                if (current_ == table_[idx_].head)
     219                {
     220                    if (idx_ < max_idx_)
     221                    {
     222                        while (!table_[++idx_].head)
     223                        { /* DUMMY BODY */ }
     224
     225                        if (idx_ < max_idx_)
     226                            current_ = table_[idx_].head;
     227                        else
     228                            current_ = nullptr;
     229                    }
     230                    else
     231                        current_ = nullptr;
     232                }
     233
     234                return hash_table_const_iterator{
     235                    table_, tmp_idx, max_idx_, tmp_current
     236                };
     237            }
     238
     239            list_node<value_type>* node()
     240            {
     241                return current_;
     242            }
     243
     244            const list_node<value_type>* node() const
     245            {
     246                return current_;
     247            }
     248
     249        private:
     250            hash_table_bucket<value_type, size_type>* table_;
     251            size_type idx_;
     252            size_type max_idx_;
     253            list_node<value_type>* current_;
     254    };
     255
     256    template<class Value, class ConstRef, class ConstPtr, class Size>
     257    bool operator==(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
     258                    const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
     259    {
     260        return lhs.node() == rhs.node();
     261    }
     262
     263    template<class Value, class ConstRef, class ConstPtr, class Size>
     264    bool operator!=(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
     265                    const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
     266    {
     267        return !(lhs == rhs);
     268    }
     269
     270    template<class Value, class Reference, class Pointer, class Size>
     271    class hash_table_iterator
     272    {
     273        public:
     274            using value_type      = Value;
     275            using size_type       = Size;
     276            using reference       = Reference;
     277            using pointer         = Pointer;
     278            using difference_type = ptrdiff_t;
     279
     280            using iterator_category = forward_iterator_tag;
     281
     282            hash_table_iterator(hash_table_bucket<value_type, size_type>* table = nullptr,
     283                                size_type idx = size_type{}, size_type max_idx = size_type{},
     284                                list_node<value_type>* current = nullptr)
     285                : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
     286            { /* DUMMY BODY */ }
     287
     288            hash_table_iterator(const hash_table_iterator&) = default;
     289            hash_table_iterator& operator=(const hash_table_iterator&) = default;
     290
     291            reference operator*()
     292            {
     293                return current_->value;
     294            }
     295
     296            pointer operator->()
     297            {
     298                return &current_->value;
     299            }
     300
     301            hash_table_iterator& operator++()
     302            {
     303                current_ = current_->next;
     304                if (current_ == table_[idx_].head)
     305                {
     306                    if (idx_ < max_idx_)
     307                    {
     308                        while (!table_[++idx_].head)
     309                        { /* DUMMY BODY */ }
     310
     311                        if (idx_ < max_idx_)
     312                            current_ = table_[idx_].head;
     313                        else
     314                            current_ = nullptr;
     315                    }
     316                    else
     317                        current_ = nullptr;
     318                }
     319
     320                return *this;
     321            }
     322
     323            hash_table_iterator operator++(int)
     324            {
     325                auto tmp_current = current_;
     326                auto tmp_idx = idx_;
     327
     328                current_ = current_->next;
     329                if (current_ == table_[idx_].head)
     330                {
     331                    if (idx_ < max_idx_)
     332                    {
     333                        while (!table_[++idx_].head)
     334                        { /* DUMMY BODY */ }
     335
     336                        if (idx_ < max_idx_)
     337                            current_ = table_[idx_].head;
     338                        else
     339                            current_ = nullptr;
     340                    }
     341                    else
     342                        current_ = nullptr;
     343                }
     344
     345                return hash_table_iterator{
     346                    table_, tmp_idx, max_idx_, tmp_current
     347                };
     348            }
     349
     350            list_node<value_type>* node()
     351            {
     352                return current_;
     353            }
     354
     355            const list_node<value_type>* node() const
     356            {
     357                return current_;
     358            }
     359
     360            template<class ConstRef, class ConstPtr>
     361            operator hash_table_const_iterator<
     362                Value, ConstRef, ConstPtr, Size
     363            >() const
     364            {
     365                return hash_table_const_iterator{
     366                    table_, idx_, max_idx_, current_
     367                };
     368            }
     369
     370        private:
     371            hash_table_bucket<value_type, size_type>* table_;
     372            size_type idx_;
     373            size_type max_idx_;
     374            list_node<value_type>* current_;
     375    };
     376
     377    template<class Value, class Ref, class Ptr, class Size>
     378    bool operator==(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
     379                    const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
     380    {
     381        return lhs.node() == rhs.node();
     382    }
     383
     384    template<class Value, class Ref, class Ptr, class Size>
     385    bool operator!=(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
     386                    const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
     387    {
     388        return !(lhs == rhs);
     389    }
     390
     391    template<class Value, class ConstReference, class ConstPointer>
     392    class hash_table_const_local_iterator
     393    {
     394        public:
     395            using value_type      = Value;
     396            using const_reference = ConstReference;
     397            using const_pointer   = ConstPointer;
     398            using difference_type = ptrdiff_t;
     399
     400            using iterator_category = forward_iterator_tag;
     401
     402            // TODO: requirement for forward iterator is default constructibility, fix others!
     403            hash_table_const_local_iterator(list_node<value_type>* head = nullptr,
     404                                            list_node<value_type>* current = nullptr)
     405                : head_{head}, current_{current}
     406            { /* DUMMY BODY */ }
     407
     408            hash_table_const_local_iterator(const hash_table_const_local_iterator&) = default;
     409            hash_table_const_local_iterator& operator=(const hash_table_const_local_iterator&) = default;
     410
     411            const_reference operator*() const
     412            {
     413                return current_->value;
     414            }
     415
     416            const_pointer operator->() const
     417            {
     418                return &current_->value;
     419            }
     420
     421            hash_table_const_local_iterator& operator++()
     422            {
     423                current_ = current_->next;
     424                if (current_ == head_)
     425                    current_ = nullptr;
     426
     427                return *this;
     428            }
     429
     430            hash_table_const_local_iterator operator++(int)
     431            {
     432                auto tmp = current_;
     433                current_ = current_->next;
     434                if (current_ == head_)
     435                    current_ = nullptr;
     436
     437                return hash_table_const_local_iterator{head_, tmp};
     438            }
     439
     440            list_node<value_type>* node()
     441            {
     442                return current_;
     443            }
     444
     445            const list_node<value_type>* node() const
     446            {
     447                return current_;
     448            }
     449
     450        private:
     451            list_node<value_type>* head_;
     452            list_node<value_type>* current_;
     453    };
     454
     455    template<class Value, class ConstRef, class ConstPtr>
     456    bool operator==(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
     457                    const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
     458    {
     459        return lhs.node() == rhs.node();
     460    }
     461
     462    template<class Value, class ConstRef, class ConstPtr>
     463    bool operator!=(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
     464                    const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
     465    {
     466        return !(lhs == rhs);
     467    }
     468
     469    template<class Value, class Reference, class Pointer>
     470    class hash_table_local_iterator
     471    {
     472        public:
     473            using value_type      = Value;
     474            using reference       = Reference;
     475            using pointer         = Pointer;
     476            using difference_type = ptrdiff_t;
     477
     478            using iterator_category = forward_iterator_tag;
     479
     480            hash_table_local_iterator(list_node<value_type>* head = nullptr,
     481                                      list_node<value_type>* current = nullptr)
     482                : head_{head}, current_{current}
     483            { /* DUMMY BODY */ }
     484
     485            hash_table_local_iterator(const hash_table_local_iterator&) = default;
     486            hash_table_local_iterator& operator=(const hash_table_local_iterator&) = default;
     487
     488            reference operator*()
     489            {
     490                return current_->value;
     491            }
     492
     493            pointer operator->()
     494            {
     495                return &current_->value;
     496            }
     497
     498            hash_table_local_iterator& operator++()
     499            {
     500                current_ = current_->next;
     501                if (current_ == head_)
     502                    current_ = nullptr;
     503
     504                return *this;
     505            }
     506
     507            hash_table_local_iterator operator++(int)
     508            {
     509                auto tmp = current_;
     510                current_ = current_->next;
     511                if (current_ == head_)
     512                    current_ = nullptr;
     513
     514                return hash_table_local_iterator{head_, tmp};
     515            }
     516
     517            list_node<value_type>* node()
     518            {
     519                return current_;
     520            }
     521
     522            const list_node<value_type>* node() const
     523            {
     524                return current_;
     525            }
     526
     527            template<class ConstRef, class ConstPtr>
     528            operator hash_table_const_local_iterator<
     529                Value, ConstRef, ConstPtr
     530            >() const
     531            {
     532                return hash_table_const_local_iterator{head_, current_};
     533            }
     534
     535        private:
     536            list_node<value_type>* head_;
     537            list_node<value_type>* current_;
     538    };
     539
     540    template<class Value, class Ref, class Ptr>
     541    bool operator==(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
     542                    const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
     543    {
     544        return lhs.node() == rhs.node();
     545    }
     546
     547    template<class Value, class Ref, class Ptr>
     548    bool operator!=(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
     549                    const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
     550    {
     551        return !(lhs == rhs);
     552    }
    111553
    112554    template<
     
    115557        class Alloc, class Size,
    116558        class Iterator, class ConstIterator,
    117         class LocalIterator, class LocalConstIterator,
     559        class LocalIterator, class ConstLocalIterator,
    118560        class Policy
    119561    >
    120562    class hash_table
    121563    {
    122         /**
    123          * What we need:
    124          *  - insert
    125          *  - emplace
    126          *  - erase
    127          *  - iterator types
    128          *  - set hint (+ clear it after each operation)
    129          *  - copy + move
    130          *  - empty/size/max_size
    131          *  - try emplace
    132          *  - insert or assign
    133          *  - clear
    134          *  - find, count
    135          *  - equal range?
    136          *  - rehash/reserve
    137          *  - bucket stuff, local iterators (use list iterators?)
    138          *  - load factor stuff
    139          *  - multi versions for operations
    140          *  - eq/neq operators (or just functions that are called in the upper
    141          *    level operator?)
    142          *  - hasher and key_eq getter
    143          */
    144564        public:
    145565            using value_type     = Value;
     
    156576            using const_local_iterator = ConstLocalIterator;
    157577
    158             hash_table(size_type buckets, float max_load_factor)
    159             {
    160                 // TODO: implement
    161             }
     578            using hint_type = tuple<
     579                hash_table_bucket<value_type, size_type>*,
     580                list_node<value_type>*, size_type
     581            >;
     582
     583            hash_table(size_type buckets, float max_load_factor = 1.f)
     584                : table_{new hash_table_bucket<value_type, size_type>[buckets]},
     585                  bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
     586                  key_extractor_{}, max_load_factor_{max_load_factor}
     587            { /* DUMMY BODY */ }
    162588
    163589            bool empty() const noexcept
     
    179605            iterator begin() noexcept
    180606            {
    181                 // TODO: implement
     607                return iterator{
     608                    table_, size_type{}, bucket_count_,
     609                    table_[0].head
     610                };
    182611            }
    183612
    184613            const_iterator begin() const noexcept
    185614            {
    186                 // TODO: implement
     615                return cbegin();
    187616            }
    188617
    189618            iterator end() noexcept
    190619            {
    191                 // TODO: implement
     620                return iterator{};
    192621            }
    193622
    194623            const_iterator end() const noexcept
    195624            {
    196                 // TODO: implement
     625                return cend();
    197626            }
    198627
    199628            const_iterator cbegin() const noexcept
    200629            {
    201                 // TODO: implement
     630                return const_iterator{
     631                    table_, size_type{}, bucket_count_,
     632                    table_[0].head
     633                };
    202634            }
    203635
    204636            const_iterator cend() const noexcept
    205637            {
    206                 // TODO: implement
     638                return const_iterator{};
    207639            }
    208640
    209641            template<class Allocator, class... Args>
    210             pair<iterator, bool> emplace(Allocator& alloc, Args&&... args)
     642            iterator emplace(Allocator& alloc, Args&&... args)
    211643            {
    212644                // TODO: use allocator traits of allocator_type but pass alloc!
     
    215647            }
    216648
    217             void insert(iterator it, value_type&& val)
    218             {
    219                 // TODO: implement, make find_for_insert that will be used with this
    220                 //       to find it to avoid unnecessary pair creations in umaps
    221                 // TODO: also, insert_or_assign should be done one level above
     649            void insert(const hint_type& where, const value_type& val)
     650            {
     651                if (!hint_ok_(where))
     652                    return;
     653
     654                auto node = new list_node<value_type>{val};
     655                if (get<1>(where) == nullptr) // Append here will create a new head.
     656                    get<0>(where)->append(node);
     657                else // Prepending before an exact position is common in the standard.
     658                    get<1>(where)->prepend(node);
     659
     660                ++size_;
     661            }
     662
     663            void insert(const hint_type& where, value_type&& val)
     664            {
     665                if (!hint_ok_(where))
     666                    return;
     667
     668                auto node = new list_node<value_type>{forward<value_type>(val)};
     669                if (get<1>(where) == nullptr)
     670                    get<0>(where)->append(node);
     671                else
     672                    get<1>(where)->prepend(node);
     673
     674                ++size_;
    222675            }
    223676
     
    234687            void clear() noexcept
    235688            {
    236                 // TODO: implement
     689                delete[] table_;
     690
     691                size_ = size_type{};
     692                table_ = new hash_table_bucket<value_type, size_type>[bucket_count_];
    237693            }
    238694
     
    243699                         noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
    244700            {
    245                 // TODO: implement
     701                std::swap(table_, other.table_);
     702                std::swap(bucket_count_, other.bucket_count_);
     703                std::swap(size_, other.size_);
     704                std::swap(hasher_, other.hasher_);
     705                std::swap(key_eq_, other.key_eq_);
     706                std::swap(max_load_factor_, other.max_load_factor_);
    246707            }
    247708
     
    268729            size_type count(const key_type& key) const
    269730            {
    270                 // TODO: implement
     731                return Policy::count(*this, key);
    271732            }
    272733
     
    294755            size_type bucket_size(size_type n) const
    295756            {
    296                 // TODO: implement
     757                return table_[n].size();
    297758            }
    298759
    299760            size_type bucket(const key_type& key) const
    300761            {
    301                 // TODO: implement
     762                return get_bucket_idx_(key);
    302763            }
    303764
    304765            local_iterator begin(size_type n)
    305766            {
    306                 // TODO: implement
     767                return local_iterator{table_[n].head, table_[n].head};
    307768            }
    308769
    309770            const_local_iterator begin(size_type n) const
    310771            {
    311                 // TODO: implement
    312             }
    313 
    314             local_iterator end(size_type n) const
    315             {
    316                 // TODO: implement
     772                return cbegin(n);
     773            }
     774
     775            local_iterator end(size_type n)
     776            {
     777                return local_iterator{};
     778            }
     779
     780            const_local_iterator end(size_type n) const
     781            {
     782                return cend(n);
    317783            }
    318784
    319785            const_local_iterator cbegin(size_type n) const
    320786            {
    321                 // TODO: implement
     787                return const_local_iterator{table_[n].head, table_[n].head};
    322788            }
    323789
    324790            const_local_iterator cend(size_type n) const
    325791            {
    326                 // TODO: implement
     792                return const_local_iterator{};
    327793            }
    328794
     
    348814            {
    349815                // TODO: implement
     816                // TODO: exception thrown in rehash means the rehash
     817                //       has no effect, so we create a new table, insert in it
     818                //       and then swap
    350819            }
    351820
     
    358827            {
    359828                // TODO: implement
     829                return false;
    360830            }
    361831
     
    372842            }
    373843
    374         private:
     844            hint_type find_insertion_spot(const key_type& key)
     845            {
     846                return Policy::find_insertion_spot(*this, key);
     847            }
     848
     849            hint_type find_insertion_spot(key_type&& key)
     850            {
     851                return Policy::find_insertion_spot(*this, key);
     852            }
     853
     854        /* private: */
    375855            hash_table_bucket<value_type, size_type>* table_;
    376856            size_type bucket_count_;
     
    378858            hasher hasher_;
    379859            key_equal key_eq_;
     860            key_extract key_extractor_;
    380861            float max_load_factor_;
    381     };
    382 
    383     template<
    384         class Key, class Value,
    385         class Hasher, class KeyEq,
    386         class Alloc, class Size
    387     >
    388     class hash_table_with_value
    389     {
    390         public:
    391             using extractor_type  = key_value_key_extractor<Key, Value>;
    392             using table_type      = hash_table<
    393                 pair<Key, Value>, extractor_type,
    394                 KeyEq, key_value_allocator, Size
    395             >;
    396 
    397         private:
    398             table_type table_;
    399             extractor_type extractor_;
    400     };
    401 
    402     template<
    403         class Key,
    404         class Hasher, class KeyEq,
    405         class Alloc, class Size
    406     >
    407     class hash_table_without_value
    408     {
    409             using extractor_type  = key_no_value_key_extractor<Key>;
    410             using table_type      = hash_table<
    411                 Key, extractor_type, KeyEq, Alloc, Size
    412             >;
    413 
    414         private:
    415             table_type table_;
     862
     863            size_type get_bucket_idx_(const key_type& key) const
     864            {
     865                return hasher_(key) % bucket_count_;
     866            }
     867
     868            bool hint_ok_(const hint_type& hint)
     869            {
     870                // TODO: pass this to the policy, because the multi policy
     871                //       will need to check if a similar key is close,
     872                //       that is something like:
     873                //          return get<1>(hint)->prev->key == key || !bucket.contains(key)
     874                return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
     875            }
     876
     877            // Praise C++11 for this.
     878            friend Policy;
    416879    };
    417880}
    418881
     882namespace std
     883{
     884    template<>
     885    struct allocator_traits<aux::key_value_allocator>
     886    {
     887        template<class Alloc, class Key, class Value, class... Args>
     888        static void construct(Alloc& alloc, pair<Key, Value>* ptr, Args&&... args)
     889        {
     890            alloc.construct(&ptr->second, forward<Args>(args)...);
     891        }
     892    };
     893}
     894
    419895#endif
Note: See TracChangeset for help on using the changeset viewer.