std::list contain splice method, that can move values from on list to another, but std::map - not. Why std::map don't have same functional? Very simple proposal - include splice method into std::map. If target map already have same keys as moved values, splice throw exception. In other case splice grant no-throw guarantee. Yes, i can move values through pair of map1.insert(*it) and map2.erase(it). But in this case my code can't grant any no-throw guarantee, because any call of std::map::insert can throw std::bad_alloc.
And there is even more (and more powerful) functionality proposed. For example one can efficiently change the value of a key using this functionality (with no deallocation/reallocation sequence).
Howard
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Howard
On 2015–10–01, at 11:26 PM, Ami Tavory <ata...@gmail.com> wrote:Sorry again if I'm misunderstanding you, but I tried to point out that I've implemented this in a libstdc++ extension which I think has been bundled with g++ for the last decade (at least I always see it in the ext/pb_ds directory of /usr/include/c++/[ver]). Incidentally, here are the performance tests for the split/join operations from the docs. Please let me know if I can help out with anything.
Additional Methods
Tree-based containers support split and join methods. It is possible to split a tree so that it passes all nodes with keys larger than a given key to a different tree. These methods have the following advantages over the alternative of externally inserting to the destination tree and erasing from the source tree:
- These methods are efficient - red-black trees are split and joined in poly-logarithmic complexity; ordered-vector trees are split and joined at linear complexity. The alternatives have super-linear complexity.
- Aside from orders of growth, these operations perform few allocations and de-allocations. For red-black trees, allocations are not performed, and the methods are exception-free.
template<typename A, typename B>
struct P
{
A a;
B b;
int foo() { return a.bar() + b.bar(); }
};
struct T
{
int bar() { return 0; }
int bar() const { return 1; }
};
union
{
P<T, T> f;
P<const T, T> s;
P<const T, const T> t;
const P<T, T> r;
} a { P<T, T>{} };
assert(a.f.foo() == 0);
assert(a.s.foo() == 1);
assert(a.t.foo() == 2);
assert(a.r.foo() == 2);
On 2015–10–01, at 11:26 PM, Ami Tavory <ata...@gmail.com> wrote:Sorry again if I'm misunderstanding you, but I tried to point out that I've implemented this in a libstdc++ extension which I think has been bundled with g++ for the last decade (at least I always see it in the ext/pb_ds directory of /usr/include/c++/[ver]). Incidentally, here are the performance tests for the split/join operations from the docs. Please let me know if I can help out with anything.
Howard seems to be simply asking for a write-up which could be properly considered by the committee. Just looking at the documentation page (perhaps overlooking source code comments), ...
Additional Methods
Tree-based containers support split and join methods. It is possible to split a tree so that it passes all nodes with keys larger than a given key to a different tree. These methods have the following advantages over the alternative of externally inserting to the destination tree and erasing from the source tree:
- These methods are efficient - red-black trees are split and joined in poly-logarithmic complexity; ordered-vector trees are split and joined at linear complexity. The alternatives have super-linear complexity.
- Aside from orders of growth, these operations perform few allocations and de-allocations. For red-black trees, allocations are not performed, and the methods are exception-free.
It looks very promising, but not yet actionable.Just as I was sending this I got your last message, and though the graphs are nice proof, they don’t seem to add any concepts.A proposal should clearly state the interface you want to add, its complexity requirements, and its relation to the existing library and other proposals like extract.Come back to this list with a draft and we’ll help to point out any gaps.For what it’s worth, I think extracting a unique_ptr (or unique_ptr-alike) and splitting a new container are orthogonal, and both would be nice to have.
On 2015–10–02, at 6:56 AM, Ami Tavory <ata...@gmail.com> wrote:I'd like to ask about the following alternative, which seems to me like something between what the paper mentions as "Talbot's original idea" and this version of the paper. IMHO, it offers improved performance for ordered containers, natural operations for multi-key containers, and offers nearly all of the benefits of node_ptr without introducing a new class.
It requires adding split and join methods to all associative containers.*{map|set} methods:void split(iterator b, iterator e, container &other)
Input: takes a range given by two iterators to the object b, e, and another empty object of the same type other;Effect: transplants the range into other, removing it from the original object.Complexity: O( (log(n))^2 ), where n is the size of the object to begin with.Exception guarantees: those of the comparison functor (this method does not allocate memory).void join(container &other)
Input: takes an object of same type other, whose keys may not be both smaller-equal and larger with those of this object.Effect: transplants the content of other into this object, emptying it.Complexity: O( (log(max(m, n))^2 ), where m, n are the sizes of the objects before the operation.Exception guarantees: those of the comparison functor (this method must not allocate memory).unordered_*{map|set} have the following methods:void split(const key &k, container &other)Input: takes a key reference k, and another empty object of the same type other;
Another point is that an empty container can basically function as a node_ptr (by splitting a single-size range into it), without introducing a new class.
For ordered containers, there are no disadvantages (no exceptions can be thrown). For unordered containers, there is a disadvantage in terms of exception safety, but it is less severe than might be thought. It's possible to configure an empty unordered associative container so that it will not throw on the first insert. Thus for a sequence of movements between containers, a single object can be pre-built, and then used repeatedly with no exception possibilities.
union
{
pair<A, B> a;
pair<const A, B> b;
} u1, u2;
new (&u1.a) pair<A, B>();
u1.a.first.foo(); //current allowed
u1.b.first.foo(); //using IntSeqRul, different member type, only read allowed
u1.b.second.bar(); //using IntSeqRul, same member type, write allowed
new (&u2.b) pair<A, B>();
u2.a.first.foo(); //illegal because it remove const