2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #ifndef CDSLIB_CONTAINER_IMPL_LAZY_KVLIST_H
32 #define CDSLIB_CONTAINER_IMPL_LAZY_KVLIST_H
35 #include <cds/container/details/guarded_ptr_cast.h>
37 namespace cds { namespace container {
39 /// Lazy ordered list (key-value pair)
40 /** @ingroup cds_nonintrusive_list
41 \anchor cds_nonintrusive_LazyKVList_gc
43 This is key-value variation of non-intrusive LazyList.
44 Like standard container, this implementation split a value stored into two part -
45 constant key and alterable value.
47 Usually, ordered single-linked list is used as a building block for the hash table implementation.
48 The complexity of searching is <tt>O(N)</tt>.
51 - \p GC - garbage collector
52 - \p Key - key type of an item to be stored in the list. It should be copy-constructible
53 - \p Value - value type to be stored in the list
54 - \p Traits - type traits, default is \p lazy_list::traits
55 It is possible to declare option-based list with cds::container::lazy_list::make_traits metafunction istead of \p Traits template
56 argument. For example, the following traits-based declaration of \p gc::HP lazy list
58 #include <cds/container/lazy_kvlist_hp.h>
59 // Declare comparator for the item
61 int operator ()( int i1, int i2 )
68 struct my_traits: public cds::container::lazy_list::traits
70 typedef my_compare compare;
73 // Declare traits-based list
74 typedef cds::container::LazyKVList< cds::gc::HP, int, int, my_traits > traits_based_list;
76 is equal to the following option-based list
78 #include <cds/container/lazy_kvlist_hp.h>
80 // my_compare is the same
82 // Declare option-based list
83 typedef cds::container::LazyKVList< cds::gc::HP, int, int,
84 typename cds::container::lazy_list::make_traits<
85 cds::container::opt::compare< my_compare > // item comparator option
91 There are different specializations of this template for each garbage collecting schema used.
92 You should include appropriate .h-file depending on GC you are using:
93 - for \p gc::HP: <tt> <cds/container/lazy_kvlist_hp.h> </tt>
94 - for \p gc::DHP: <tt> <cds/container/lazy_kvlist_dhp.h> </tt>
95 - for \ref cds_urcu_desc "RCU": <tt> <cds/container/lazy_kvlist_rcu.h> </tt>
96 - for \p gc::nogc: <tt> <cds/container/lazy_kvlist_nogc.h> </tt>
102 #ifdef CDS_DOXYGEN_INVOKED
103 typename Traits = lazy_list::traits
109 #ifdef CDS_DOXYGEN_INVOKED
110 protected intrusive::LazyList< GC, implementation_defined, Traits >
112 protected details::make_lazy_kvlist< GC, Key, Value, Traits >::type
116 typedef details::make_lazy_kvlist< GC, Key, Value, Traits > maker;
117 typedef typename maker::type base_class;
121 typedef GC gc; ///< Garbage collector
122 typedef Traits traits; ///< Traits
123 #ifdef CDS_DOXYGEN_INVOKED
124 typedef Key key_type ; ///< Key type
125 typedef Value mapped_type ; ///< Type of value stored in the list
126 typedef std::pair<key_type const, mapped_type> value_type ; ///< key/value pair stored in the list
128 typedef typename maker::key_type key_type;
129 typedef typename maker::mapped_type mapped_type;
130 typedef typename maker::value_type value_type;
132 typedef typename base_class::back_off back_off; ///< Back-off strategy
133 typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes
134 typedef typename base_class::item_counter item_counter; ///< Item counter type
135 typedef typename maker::key_comparator key_comparator; ///< key comparing functor
136 typedef typename base_class::memory_model memory_model; ///< Memory ordering. See \p cds::opt::memory_model
137 typedef typename base_class::stat stat; ///< Internal statistics
139 static CDS_CONSTEXPR const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm
142 // Rebind traits (split-list support)
143 template <typename... Options>
144 struct rebind_traits {
147 , key_type, mapped_type
148 , typename cds::opt::make_options< traits, Options...>::type
153 template <typename Stat>
154 using select_stat_wrapper = typename base_class::template select_stat_wrapper< Stat >;
159 typedef typename base_class::value_type node_type;
160 typedef typename maker::cxx_allocator cxx_allocator;
161 typedef typename maker::node_deallocator node_deallocator;
162 typedef typename maker::intrusive_traits::compare intrusive_key_comparator;
164 typedef typename base_class::node_type head_type;
169 typedef typename gc::template guarded_ptr< node_type, value_type, details::guarded_ptr_cast_map<node_type, value_type> > guarded_ptr;
173 template <typename K>
174 static node_type * alloc_node(const K& key)
176 return cxx_allocator().New( key );
179 template <typename K, typename V>
180 static node_type * alloc_node( const K& key, const V& val )
182 return cxx_allocator().New( key, val );
185 template <typename... Args>
186 static node_type * alloc_node( Args&&... args )
188 return cxx_allocator().MoveNew( std::forward<Args>(args)... );
191 static void free_node( node_type * pNode )
193 cxx_allocator().Delete( pNode );
196 struct node_disposer {
197 void operator()( node_type * pNode )
202 typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr;
206 return base_class::m_Head;
209 head_type const& head() const
211 return base_class::m_Head;
216 return base_class::m_Tail;
219 head_type const& tail() const
221 return base_class::m_Tail;
228 template <bool IsConst>
229 class iterator_type: protected base_class::template iterator_type<IsConst>
231 typedef typename base_class::template iterator_type<IsConst> iterator_base;
233 iterator_type( head_type const& pNode )
234 : iterator_base( const_cast<head_type *>(&pNode) )
236 iterator_type( head_type const * pNode )
237 : iterator_base( const_cast<head_type *>(pNode) )
240 friend class LazyKVList;
243 typedef typename cds::details::make_const_type<mapped_type, IsConst>::reference value_ref;
244 typedef typename cds::details::make_const_type<mapped_type, IsConst>::pointer value_ptr;
246 typedef typename cds::details::make_const_type<value_type, IsConst>::reference pair_ref;
247 typedef typename cds::details::make_const_type<value_type, IsConst>::pointer pair_ptr;
252 iterator_type( iterator_type const& src )
253 : iterator_base( src )
256 key_type const& key() const
258 typename iterator_base::value_ptr p = iterator_base::operator ->();
259 assert( p != nullptr );
260 return p->m_Data.first;
263 value_ref val() const
265 typename iterator_base::value_ptr p = iterator_base::operator ->();
266 assert( p != nullptr );
267 return p->m_Data.second;
270 pair_ptr operator ->() const
272 typename iterator_base::value_ptr p = iterator_base::operator ->();
273 return p ? &(p->m_Data) : nullptr;
276 pair_ref operator *() const
278 typename iterator_base::value_ref p = iterator_base::operator *();
283 iterator_type& operator ++()
285 iterator_base::operator ++();
290 bool operator ==(iterator_type<C> const& i ) const
292 return iterator_base::operator ==(i);
295 bool operator !=(iterator_type<C> const& i ) const
297 return iterator_base::operator !=(i);
305 The forward iterator for lazy list has some features:
306 - it has no post-increment operator
307 - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator.
308 For some GC (\p gc::HP), a guard is limited resource per thread, so an exception (or assertion) "no free guard"
309 may be thrown if a limit of guard count per thread is exceeded.
310 - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data.
311 - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
312 deleting operations it is no guarantee that you iterate all item in the list.
314 @warning Use this iterator on the concurrent container for debugging purpose only.
316 The iterator interface to access item data:
317 - <tt> operator -> </tt> - returns a pointer to \ref value_type for iterator
318 - <tt> operator *</tt> - returns a reference (a const reference for \p const_iterator) to \ref value_type for iterator
319 - <tt> const key_type& key() </tt> - returns a key reference for iterator
320 - <tt> mapped_type& val() </tt> - retuns a value reference for iterator (const reference for \p const_iterator)
322 For both functions the iterator should not be equal to <tt> end() </tt>
324 typedef iterator_type<false> iterator;
326 /// Const forward iterator
328 For iterator's features and requirements see \ref iterator
330 typedef iterator_type<true> const_iterator;
332 ///@name Forward iterators (only for debugging purpose)
334 /// Returns a forward iterator addressing the first element in a list
336 For empty list \code begin() == end() \endcode
340 iterator it( head() );
341 ++it ; // skip dummy head
345 /// Returns an iterator that addresses the location succeeding the last element in a list
347 Do not use the value returned by <tt>end</tt> function to access any item.
348 Internally, <tt>end</tt> returning value equals to \p nullptr.
350 The returned value can be used only to control reaching the end of the list.
351 For empty list \code begin() == end() \endcode
355 return iterator( tail() );
358 /// Returns a forward const iterator addressing the first element in a list
359 const_iterator begin() const
361 const_iterator it( head() );
362 ++it; // skip dummy head
366 /// Returns a forward const iterator addressing the first element in a list
367 const_iterator cbegin() const
369 const_iterator it( head() );
370 ++it; // skip dummy head
374 /// Returns an const iterator that addresses the location succeeding the last element in a list
375 const_iterator end() const
377 return const_iterator( tail());
380 /// Returns an const iterator that addresses the location succeeding the last element in a list
381 const_iterator cend() const
383 return const_iterator( tail());
388 /// Default constructor
393 template <typename Stat, typename = std::enable_if<std::is_same<stat, lazy_list::wrapped_stat<Stat>>::value >>
394 explicit LazyKVList( Stat& st )
399 /// Destructor clears the list
405 /// Inserts new node with key and default value
407 The function creates a node with \p key and default value, and then inserts the node created into the list.
410 - The \ref key_type should be constructible from value of type \p K.
411 In trivial case, \p K is equal to \ref key_type.
412 - The \ref mapped_type should be default-constructible.
414 Returns \p true if inserting successful, \p false otherwise.
416 template <typename K>
417 bool insert( K&& key )
419 return insert_at( head(), std::forward<K>( key ));
422 /// Inserts new node with a key and a value
424 The function creates a node with \p key and value \p val, and then inserts the node created into the list.
427 - The \ref key_type should be constructible from \p key of type \p K.
428 - The \ref mapped_type should be constructible from \p val of type \p V.
430 Returns \p true if inserting successful, \p false otherwise.
432 template <typename K, typename V>
433 bool insert( K&& key, V&& val )
435 // We cannot use insert with functor here
436 // because we cannot lock inserted node for updating
437 // Therefore, we use separate function
438 return insert_at( head(), std::forward<K>( key ), std::forward<V>( val ));
441 /// Inserts new node and initializes it by a functor
443 This function inserts new node with key \p key and if inserting is successful then it calls
444 \p func functor with signature
447 void operator()( value_type& item );
451 The argument \p item of user-defined functor \p func is the reference
452 to the list's item inserted. <tt>item.second</tt> is a reference to item's value that may be changed.
453 The user-defined functor is called only if inserting is successful.
455 The \p key_type should be constructible from value of type \p K.
457 The function allows to split creating of new item into two part:
458 - create item from \p key;
459 - insert new item into the list;
460 - if inserting is successful, initialize the value of item by calling \p func functor
462 This can be useful if complete initialization of object of \p mapped_type is heavyweight and
463 it is preferable that the initialization should be completed only if inserting is successful.
465 template <typename K, typename Func>
466 bool insert_with( K&& key, Func func )
468 return insert_with_at( head(), std::forward<K>( key ), func );
471 /// Inserts data of type \ref mapped_type constructed with <tt>std::forward<Args>(args)...</tt>
473 Returns \p true if inserting successful, \p false otherwise.
475 template <typename... Args>
476 bool emplace( Args&&... args )
478 return emplace_at( head(), std::forward<Args>(args)... );
481 /// Updates data by \p key
483 The operation performs inserting or replacing the element with lock-free manner.
485 If the \p key not found in the list, then the new item created from \p key
486 will be inserted iff \p bAllowInsert is \p true.
487 (note that in this case the \ref key_type should be constructible from type \p K).
488 Otherwise, if \p key is found, the functor \p func is called with item found.
490 The functor \p Func signature is:
493 void operator()( bool bNew, value_type& item );
497 - \p bNew - \p true if the item has been inserted, \p false otherwise
498 - \p item - the item found or inserted
500 The functor may change any fields of the \p item.second of \p mapped_type;
501 during \p func call \p item is locked so it is safe to modify the item in
502 multi-threaded environment.
504 Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successful,
505 \p second is true if new item has been added or \p false if the item with \p key
508 template <typename K, typename Func>
509 std::pair<bool, bool> update( K&& key, Func f, bool bAllowInsert = true )
511 return update_at( head(), std::forward<K>( key ), f, bAllowInsert );
514 template <typename K, typename Func>
515 CDS_DEPRECATED("ensure() is deprecated, use update()")
516 std::pair<bool, bool> ensure( const K& key, Func f )
518 return update( key, f, true );
522 /// Deletes \p key from the list
523 /** \anchor cds_nonintrusive_LazyKVList_hp_erase_val
525 Returns \p true if \p key is found and has been deleted, \p false otherwise
527 template <typename K>
528 bool erase( K const& key )
530 return erase_at( head(), key, intrusive_key_comparator() );
533 /// Deletes the item from the list using \p pred predicate for searching
535 The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_erase_val "erase(K const&)"
536 but \p pred is used for key comparing.
537 \p Less functor has the interface like \p std::less.
538 \p pred must imply the same element order as the comparator used for building the list.
540 template <typename K, typename Less>
541 bool erase_with( K const& key, Less pred )
544 return erase_at( head(), key, typename maker::template less_wrapper<Less>::type() );
547 /// Deletes \p key from the list
548 /** \anchor cds_nonintrusive_LazyKVList_hp_erase_func
549 The function searches an item with key \p key, calls \p f functor with item found
550 and deletes it. If \p key is not found, the functor is not called.
552 The functor \p Func interface:
555 void operator()(value_type& val) { ... }
559 Returns \p true if key is found and deleted, \p false otherwise
561 template <typename K, typename Func>
562 bool erase( K const& key, Func f )
564 return erase_at( head(), key, intrusive_key_comparator(), f );
567 /// Deletes the item from the list using \p pred predicate for searching
569 The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_erase_func "erase(K const&, Func)"
570 but \p pred is used for key comparing.
571 \p Less functor has the interface like \p std::less.
572 \p pred must imply the same element order as the comparator used for building the list.
574 template <typename K, typename Less, typename Func>
575 bool erase_with( K const& key, Less pred, Func f )
578 return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
581 /// Extracts the item from the list with specified \p key
582 /** \anchor cds_nonintrusive_LazyKVList_hp_extract
583 The function searches an item with key equal to \p key,
584 unlinks it from the list, and returns it as \p guarded_ptr.
585 If \p key is not found the function returns an empty guarded pointer.
587 Note the compare functor should accept a parameter of type \p K that can be not the same as \p key_type.
589 @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
593 typedef cds::container::LazyKVList< cds::gc::HP, int, foo, my_traits > ord_list;
597 ord_list::guarded_ptr gp( theList.extract( 5 ));
602 // Destructor of gp releases internal HP guard and frees the item
606 template <typename K>
607 guarded_ptr extract( K const& key )
609 return extract_at( head(), key, intrusive_key_comparator() );
612 /// Extracts the item from the list with comparing functor \p pred
614 The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_extract "extract(K const&)"
615 but \p pred predicate is used for key comparing.
617 \p Less functor has the semantics like \p std::less but should take arguments of type \ref key_type and \p K
619 \p pred must imply the same element order as the comparator used for building the list.
621 template <typename K, typename Less>
622 guarded_ptr extract_with( K const& key, Less pred )
625 return extract_at( head(), key, typename maker::template less_wrapper<Less>::type() );
628 /// Checks whether the list contains \p key
630 The function searches the item with key equal to \p key
631 and returns \p true if it is found, and \p false otherwise.
633 template <typename Q>
634 bool contains( Q const& key )
636 return find_at( head(), key, intrusive_key_comparator() );
639 template <typename Q>
640 CDS_DEPRECATED("deprecated, use contains()")
641 bool find( Q const& key )
643 return contains( key );
647 /// Checks whether the map contains \p key using \p pred predicate for searching
649 The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
650 \p Less functor has the interface like \p std::less.
651 \p Less must imply the same element order as the comparator used for building the list.
653 template <typename Q, typename Less>
654 bool contains( Q const& key, Less pred )
657 return find_at( head(), key, typename maker::template less_wrapper<Less>::type() );
660 template <typename Q, typename Less>
661 CDS_DEPRECATED("deprecated, use contains()")
662 bool find_with( Q const& key, Less pred )
664 return contains( key, pred );
668 /// Finds the key \p key and performs an action with it
669 /** \anchor cds_nonintrusive_LazyKVList_hp_find_func
670 The function searches an item with key equal to \p key and calls the functor \p f for the item found.
671 The interface of \p Func functor is:
674 void operator()( value_type& item );
677 where \p item is the item found.
679 The functor may change <tt>item.second</tt> that is reference to value of node.
680 Note that the function is only guarantee that \p item cannot be deleted during functor is executing.
681 The function does not serialize simultaneous access to the list \p item. If such access is
682 possible you must provide your own synchronization schema to exclude unsafe item modifications.
684 The function returns \p true if \p key is found, \p false otherwise.
686 template <typename Q, typename Func>
687 bool find( Q const& key, Func f )
689 return find_at( head(), key, intrusive_key_comparator(), f );
692 /// Finds the key \p val using \p pred predicate for searching
694 The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_find_func "find(Q&, Func)"
695 but \p pred is used for key comparing.
696 \p Less functor has the interface like \p std::less.
697 \p pred must imply the same element order as the comparator used for building the list.
699 template <typename Q, typename Less, typename Func>
700 bool find_with( Q const& key, Less pred, Func f )
703 return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
706 /// Finds \p key and return the item found
707 /** \anchor cds_nonintrusive_LazyKVList_hp_get
708 The function searches the item with key equal to \p key
709 and returns the item found as a guarded pointer.
710 If \p key is not found the functions returns an empty \p guarded_ptr.
712 @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
716 typedef cds::container::LazyKVList< cds::gc::HP, int, foo, my_traits > ord_list;
720 ord_list::guarded_ptr gp( theList.get( 5 ));
725 // Destructor of guarded_ptr releases internal HP guard and frees the item
729 Note the compare functor specified for class \p Traits template parameter
730 should accept a parameter of type \p K that can be not the same as \p key_type.
732 template <typename K>
733 guarded_ptr get( K const& key )
735 return get_at( head(), key, intrusive_key_comparator() );
738 /// Finds the key \p val and return the item found
740 The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_get "get(K const&)"
741 but \p pred is used for comparing the keys.
743 \p Less functor has the semantics like \p std::less but should take arguments of type \ref key_type and \p K
745 \p pred must imply the same element order as the comparator used for building the list.
747 template <typename K, typename Less>
748 guarded_ptr get_with( K const& key, Less pred )
751 return get_at( head(), key, typename maker::template less_wrapper<Less>::type() );
754 /// Checks if the list is empty
757 return base_class::empty();
760 /// Returns list's item count
762 The value returned depends on opt::item_counter option. For atomicity::empty_item_counter,
763 this function always returns 0.
765 @note Even if you use real item counter and it returns 0, this fact is not mean that the list
766 is empty. To check list emptyness use \ref empty() method.
770 return base_class::size();
773 /// Returns const reference to internal statistics
774 stat const& statistics() const
776 return base_class::statistics();
787 bool insert_node_at( head_type& refHead, node_type * pNode )
789 assert( pNode != nullptr );
790 scoped_node_ptr p( pNode );
792 if ( base_class::insert_at( &refHead, *p )) {
800 template <typename K>
801 bool insert_at( head_type& refHead, K&& key )
803 return insert_node_at( refHead, alloc_node( std::forward<K>( key )));
806 template <typename K, typename V>
807 bool insert_at( head_type& refHead, K&& key, V&& val )
809 return insert_node_at( refHead, alloc_node( std::forward<K>( key ), std::forward<V>( val )));
812 template <typename K, typename Func>
813 bool insert_with_at( head_type& refHead, K&& key, Func f )
815 scoped_node_ptr pNode( alloc_node( std::forward<K>( key )));
817 if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ f( node.m_Data ); } )) {
824 template <typename... Args>
825 bool emplace_at( head_type& refHead, Args&&... args )
827 return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
830 template <typename K, typename Compare>
831 bool erase_at( head_type& refHead, K const& key, Compare cmp )
833 return base_class::erase_at( &refHead, key, cmp );
836 template <typename K, typename Compare, typename Func>
837 bool erase_at( head_type& refHead, K const& key, Compare cmp, Func f )
839 return base_class::erase_at( &refHead, key, cmp, [&f](node_type const & node){f( const_cast<value_type&>(node.m_Data)); });
842 template <typename K, typename Compare>
843 guarded_ptr extract_at( head_type& refHead, K const& key, Compare cmp )
845 return base_class::extract_at( &refHead, key, cmp );
848 template <typename K, typename Func>
849 std::pair<bool, bool> update_at( head_type& refHead, K&& key, Func f, bool bAllowInsert )
851 scoped_node_ptr pNode( alloc_node( std::forward<K>( key )));
853 std::pair<bool, bool> ret = base_class::update_at( &refHead, *pNode,
854 [&f]( bool bNew, node_type& node, node_type& ){ f( bNew, node.m_Data ); },
856 if ( ret.first && ret.second )
862 template <typename K, typename Compare>
863 bool find_at( head_type& refHead, K const& key, Compare cmp )
865 return base_class::find_at( &refHead, key, cmp );
868 template <typename K, typename Compare, typename Func>
869 bool find_at( head_type& refHead, K& key, Compare cmp, Func f )
871 return base_class::find_at( &refHead, key, cmp, [&f]( node_type& node, K& ){ f( node.m_Data ); });
874 template <typename K, typename Compare>
875 guarded_ptr get_at( head_type& refHead, K const& key, Compare cmp )
877 return base_class::get_at( &refHead, key, cmp );
883 }} // namespace cds::container
885 #endif // #ifndef CDSLIB_CONTAINER_IMPL_LAZY_KVLIST_H