diff --git a/doc/intrusive.qbk b/doc/intrusive.qbk index a6d7990..cb8f502 100644 --- a/doc/intrusive.qbk +++ b/doc/intrusive.qbk @@ -1945,75 +1945,109 @@ the same options explained in the section [*Boost.Intrusive] associative containers offer an interface similar to STL associative containers. However, STL's ordered and unordered simple associative containers (`std::set`, `std::multiset`, `std::unordered_set` and `std::unordered_multiset`) -have some inefficiencies caused by the interface in several search functions -(`equal_range`, `lower_bound`, `upper_bound`, ...): the user can only operate with -`value_type` objects or (starting from C++11), +have some inefficiencies caused by the interface in several search, insertion or erasure functions +(`equal_range`, `lower_bound`, `upper_bound`, `find`, `insert`, `erase`...): the user can only operate +with `value_type` objects or (starting from C++11), [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3657.htm heterogeneous comparison lookups] which are not flexible enough as `key_compare` shall support the comparison between the provided -key and `value_type`, avoiding the use of user-defined comparison objects that can partition the -search in advanced ways. +key and `value_type`, which precludes the use of user-defined comparison objects that can partition the +search in a compatible but advanced way. -Imagine that the object to be searched is quite expensive to construct: +To solve these problems, [*Boost.Intrusive] containers offers functions where a key type different +from `key_type` and a comparison object are provided by the user. This applies to: + * equal_range + * lower_bound + * upper_bound + * count + * find + * erase + +Requirements for such functions are: + +* For unordered container the provided comparison and hashing + function with the given key shall induce the same hash and equivalence as `key_compare` and `hasher`. + +* For ordered associative containers, lookup and erasure functions, the container to be searched shall + be partitioned in regards to the supplied comparison object and key. + +For more details, see [*Requires] clauses of such functions in the reference. + +[section:advanced_lookups_example Example] + +Imagine that the object to be searched is quite expensive to construct (called `Expensive` in the example): [import ../example/doc_assoc_optimized_code.cpp] [doc_assoc_optimized_code_normal_find] -`Expensive` is an expensive object to construct. If "key" c-string is quite long +If "key" c-string is quite long `Expensive` has to construct a `std::string` using heap memory. Like `Expensive`, many times the only member taking part in ordering issues is just -a small part of the class. For example, with `Expensive`, only the internal +a small part of the class. E.g.: with `Expensive`, only the internal `std::string` is needed to compare the object. In both containers, if we call `get_from_set/get_from_unordered_set` in a loop, we might get a performance penalty, because we are forced to create a whole `Expensive` object to be able to find an equivalent one. -Sometimes this interface limitation is severe, because +Sometimes the problem is not only performance-related, as we [*might not have enough information to construct the object] but we might [*have enough information to find the object]. In this case, a name is enough -to search `Expensive` in the container but constructing an `Expensive` object -might requires more information that the user might not have. +to search `Expensive` objects in the container but constructing an `Expensive` +object might require more information that the searcher might not have. -To solve this, [*Boost.Intrusive] associative containers -offer alternative functions, which take any type comparable with the value and a -functor that should be `compatible` -(the associative container must be also partitioned in regards to the supplied comparison type) -with container's predicate function (`key_compare`). -[classref boost::intrusive::unordered_set unordered_set]/[classref boost::intrusive::unordered_multiset unordered_multiset] -offer similar functions that take any key type, a compatible hash (the hash of the key) and a equality function. Now, let's see -optimized search function: +To solve this, we can use the functions that take any type comparable with the value and a +the 'compatible' comparison object (and hash, when the associative container is unordered) +Let's see optimized search function: [doc_assoc_optimized_code_optimized_find] -This new arbitrary key overload is also available for other functions taking -values as arguments: - -* equal_range -* lower_bound -* upper_bound -* count -* find -* erase - -Check [classref boost::intrusive::set set], -[classref boost::intrusive::multiset multiset], -[classref boost::intrusive::unordered_set unordered_set], -[classref boost::intrusive::unordered_multiset unordered_multiset] -references to know more about those functions. +[endsect] [endsect] [section:advanced_insertions Advanced insertions] A similar issue happens with insertions in simple ordered and unordered associative -containers with unique keys (`std::set` and `std::tr1::unordered_set`). In these containers, +containers with unique keys (`std::set` and `std::unordered_set`). In these containers, if a value is already present, the value to be inserted is discarded. With expensive values, if the value is already present, we can suffer efficiency problems. -[classref boost::intrusive::set set] and [classref boost::intrusive::unordered_set unordered_set] -have insertion functions to check efficiently, without +[classref boost::intrusive::set set] and [classref boost::intrusive::unordered_set unordered_set]-like +containers have insertion functions (`insert_check`, `insert_unique_check`,...) to check efficiently, without constructing the value, if a value is present or not and if it's not present, a -function to insert it immediately without any further lookup. +function to insert it immediately (`insert_commit`) without any further lookup. Requirements for functions +that check the existence of such value are: + +* For unordered container the provided comparison and hashing + function with the given key shall induce the same hash and equivalence as `key_compare` and `hasher`. + +* For ordered associative containers, the provided comparison function with the given key, shall induce the same +strict weak order as `key_compare`. + +To sum up, `insert_check` is similar to a normal `insert` but: + +* `insert_check` can be used with arbitrary keys +* if the insertion is possible (there is no equivalent value) `insert_check` collects all the needed information +in an `insert_commit_data` structure, so that `insert_commit`: + * [*does not execute] further comparisons + * can be executed with [*constant-time complexity] + * has [*no-throw guarantee]. + +These functions must be used with care, +no other insertion or erasure must be executed between an `insert_check` and an `insert_commit` +pair. Otherwise, the behaviour is undefined. + +See [classref boost::intrusive::set set] +and [classref boost::intrusive::unordered_set unordered_set]-like containers' reference +for more information about `insert_check` and `insert_commit`. + +With multiple ordered and unordered associative containers +([classref boost::intrusive::multiset multiset] and +[classref boost::intrusive::unordered_multiset unordered_multiset]) there is +no need for these advanced insertion functions, since insertions are always successful. + +[section:advanced_insertions_example Example] + For example, using the same `Expensive` class, this function can be inefficient: @@ -2025,28 +2059,7 @@ will be discarded, and this is a waste of resources. Instead of that, let's use [doc_assoc_optimized_code_optimized_insert] -`insert_check` is similar to a normal `insert` but: - -* `insert_check` can be used with arbitrary keys -* if the insertion is possible (there is no equivalent value) `insert_check` collects all the needed information -in an `insert_commit_data` structure, so that `insert_commit`: - * [*does not execute] further comparisons - * can be executed with [*constant-time complexity] - * has [*no-throw guarantee]. - -These functions must be used with care, since -no other insertion or erasure must be executed between an `insert_check` and an `insert_commit` -pair. Otherwise, the behaviour is undefined. -`insert_check` and `insert_commit` will come in handy -for developers programming efficient non-intrusive associative containers. -See [classref boost::intrusive::set set] -and [classref boost::intrusive::unordered_set unordered_set] reference for more information about -`insert_check` and `insert_commit`. - -With multiple ordered and unordered associative containers -([classref boost::intrusive::multiset multiset] and -[classref boost::intrusive::unordered_multiset unordered_multiset]) there is -no need for these advanced insertion functions, since insertions are always successful. +[endsect] [endsect] @@ -2062,8 +2075,8 @@ from other associative containers: if the ordering and uniqueness properties are there is no need to waste time checking the position of each source value, because values are already ordered: back insertions will be much more efficient. -[*Note:] These functions [*don't check preconditions] so they must used with care. These -are functions are low-level operations [*will break container invariants if +[*Note:] These functions [*don't check preconditions] so they must used with care. They +are low-level operations that [*will break container invariants if ordering and uniqueness preconditions are not assured by the caller.] Let's see an example: @@ -2071,7 +2084,6 @@ Let's see an example: [import ../example/doc_positional_insertion.cpp] [doc_positional_insertion] - [endsect] For more information about advanced lookup and insertion functions see diff --git a/include/boost/intrusive/bstree.hpp b/include/boost/intrusive/bstree.hpp index 0934d86..b08c49b 100644 --- a/include/boost/intrusive/bstree.hpp +++ b/include/boost/intrusive/bstree.hpp @@ -1386,7 +1386,7 @@ class bstree_impl { return this->erase(key, this->key_comp()); } //! Requires: key is a value such that `*this` is partitioned with respect to - //! comp(nk, key) and !c(key, nk), with c(nk, key) implying !c(key, nk), + //! comp(nk, key) and !comp(key, nk), with comp(nk, key) implying !comp(key, nk), //! with nk the key_type of a value_type inserted into `*this`. //! //! Effects: Erases all the elements with the given key. @@ -1470,7 +1470,7 @@ class bstree_impl { size_type n; return this->private_erase(b, e, n, disposer); } //! Requires: key is a value such that `*this` is partitioned with respect to - //! comp(nk, key) and !c(key, nk), with c(nk, key) implying !c(key, nk) + //! comp(nk, key) and !comp(key, nk), with comp(nk, key) implying !comp(key, nk) //! and nk the key_type of a value_type inserted into `*this`. //! //! Requires: Disposer::operator()(pointer) shouldn't throw. @@ -1546,7 +1546,7 @@ class bstree_impl { return size_type(this->count(key, this->key_comp())); } //! Requires: key is a value such that `*this` is partitioned with respect to - //! comp(nk, key) and !c(key, nk), with c(nk, key) implying !c(key, nk), + //! comp(nk, key) and !comp(key, nk), with comp(nk, key) implying !comp(key, nk), //! and nk the key_type of a value_type inserted into `*this`. //! //! Effects: Returns the number of contained elements with the given key @@ -1643,7 +1643,7 @@ class bstree_impl iterator find(const key_type &key); //! Requires: key is a value such that `*this` is partitioned with respect to - //! comp(nk, key) and !c(key, nk), with c(nk, key) implying !c(key, nk), + //! comp(nk, key) and !comp(key, nk), with comp(nk, key) implying !comp(key, nk), //! and nk the key_type of a value_type inserted into `*this`. //! //! Effects: Finds an iterator to the first element whose key is @@ -1672,7 +1672,7 @@ class bstree_impl std::pair equal_range(const key_type &key); //! Requires: key is a value such that `*this` is partitioned with respect to - //! comp(nk, key) and !c(key, nk), with c(nk, key) implying !c(key, nk), + //! comp(nk, key) and !comp(key, nk), with comp(nk, key) implying !comp(key, nk), //! with nk the key_type of a value_type inserted into `*this`. //! //! Effects: Finds a range containing all elements whose key is k or