4

I need to replace specific key values, while the rest of the value_type is left untouched. What I actually need to do, is copy the value, erase the entry and insert it with changed key value again. This is absolutely bad. I need to copy the whole value_type twice, and deallocate/allocate again.

Why the standard doesn't define methods like this:

// returns count of exchanged keys
size_type exchange_key(key_type const& x, key_type const& y);
// returns count of replaced keys
size_type replace_key(key_type const& old_key, key_type const& new_key);

Is there anything I'm missing?

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
0xbadf00d
  • 17,405
  • 15
  • 67
  • 107
  • You can insert first, then erase. Probably one less copy of the value (also probably more exception-safe). – visitor Jul 22 '11 at 08:32
  • @visitor: and in C++0x, I think (without checking) you can probably move the old value to the new one, then erase. Exception safe, and no copies at all. – Steve Jessop Jul 22 '11 at 08:55
  • @Steve Jessop - I don't know how this should be possible with moving, because the values and keys are combined together in the `std::pair` structure. – 0xbadf00d Jul 22 '11 at 09:22
  • I don't see how the pair impedes anything. `mymap.insert(make_pair(new_key, move(map[old_key])))`, or something like that. Then erase the old one. Actually, that's not exception-safe, since if the `insert` fails then the old value has already been moved and trashed, but the exception-safe code doesn't fit in this margin ;-) Or does `std::pair` not have a move constructor that moves the elements? – Steve Jessop Jul 22 '11 at 09:29
  • @Steve Jessop - Sure, there is a move constructor for `std::pair`, but it dependends on the `second_type` wheter this will be more performant or not. – 0xbadf00d Jul 22 '11 at 10:03

6 Answers6

2

Now, you can, with .extract(key) (since C++17). https://en.cppreference.com/w/cpp/container/map/extract

VTiTux
  • 311
  • 1
  • 11
1

I don't why it was not added in the first place, and i understand that it is too bad. I guess they just added what they felt was absolutely necessary.

I think i have read somewhere that Boost.MultiIndex provided this ability.

Benoît
  • 16,798
  • 8
  • 46
  • 66
1

Associative containers are implemented in a way that does not allow to change the 'key' in an efficient manner. To make this explicit it does not provide convienence methods to replace a key. The associative container would also have to remove and insert again under the covers.

frast
  • 2,700
  • 1
  • 25
  • 34
1

I think this is an abstraction problem. The standard doesn't say exactly how the containers are to be implemented, it only specifies the maximum complexity of some of the operations and leaves the rest to the implementation.

If the standard were to add a replace_key function, it would also have to specify that this should have a different complexity than the existing erase-insert combination. How can it do that without leaking implementation details? If the function isn't guaranteed to be faster on all implementations, it is pretty useless.

When you say that it would obviously be faster, you make assumptions about implementation details that the standard tries to avoid.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • 1
    "it would also have to specify that this should have a different complexity than the existing erase-insert combination" - it couldn't possibly do this, a complexity faster than `O(log n)`, which is the complexity of insert+remove, would be unrealistic for replace_key. However, I disagree that the standards body believes that convenience functions must have better complexity in order to be worth adding to the standard. Consider `operator[]`, which doesn't have better complexity than the equivalent find+insert. – Steve Jessop Jul 22 '11 at 08:50
  • @Steve Jessop - I think what he actually meant was, a convenience function shouldn't have worse complexity than a combination of equivalent functions. – 0xbadf00d Jul 22 '11 at 09:19
0

This is because changing a key could affect the structure of an associative containers. Notably, std::map, which is a typically Red-Black tree, the tree structure mostly will be changed once you modify a key (e.g., rotating sub trees). In some data structures, even such dynamic changes are disallowed. So, it is challenging to expose such operation as a standard in an associative container.

Regarding the overhead you concerned, once you have value_type as a pointer or reference, the overhead of deleting/inserting a pair isn't too bad.

minjang
  • 8,860
  • 9
  • 42
  • 61
  • Why would this be disallowed? It actually comes down to a delete/insert operation without copying the value object. – KillianDS Jul 22 '11 at 08:38
  • I'm not saying typical STL does. But, some data structures, say range tress, it could be a static, which doesn't allow dynamic inserting/deleting, once after construction. – minjang Jul 22 '11 at 08:45
  • http://www.cgal.org/Manual/latest/doc_html/cgal_manual/SearchStructures/Chapter_main.html – minjang Jul 22 '11 at 08:45
  • @Killian - You make assumptions about what the internal structure of the container is. The standard does not go into such details. – Bo Persson Jul 22 '11 at 08:49
  • 1
    @Bo: afaik erase/insert are quite obligated by the standard? – KillianDS Jul 22 '11 at 08:52
  • @Killian - Yes, but the OP wants to "move" objects around in the container without using erase and insert. To do that, we must know how the data is stored, and the standard avoids specifying that. – Bo Persson Jul 22 '11 at 08:57
  • @Bo: No, actually he wants to move objects around without the value being copied. He never told the erase/insert idiom was bad, also see steve jessop's comment on your answer. – KillianDS Jul 22 '11 at 08:59
  • Sorry, have to agree: this answer doesn't make sense. The current standard ('03) defines 4 associative containers, and for all those the replace operation is possible, and can be implemented using the existing methods. – MSalters Jul 22 '11 at 09:01
  • @minjang - The insertion or reordering, which would be necessary when the key value is changed, couldn't be less performant thant a erase+insert. – 0xbadf00d Jul 22 '11 at 09:21
0

Well, honestly behind the screens it would result into an insert and delete operation anyhow, with the sole difference that the value-part will not be copied. While this seems to be your biggest concern, unless your object is very heavy on copying, in a large container, the update operation to re-stabilize the ordered container will be heavier anyhow.

Now this would require some important changes however going further than the associative containers, the two most important I can see are:

  1. The std::pair class needs an update, because you must be able to update the key without creating a new pair (as this would also copy the value object).
  2. You need an update function that removes a pair from the tree, calls the new logic from 1., and reinserts it.

I think the main problem lies with the first one, as std::pair is at the moment actually a very simple wrapper, and your proposal would delete that principle and add some (unnecessary) complexity to it. Also note that call 2 does not actually add new functionality but wraps a system the developer could manage himself easily through references and the like. If they would add all this wrapping to std, it would become a really huge bloated piece of library.

If you want this principle, you should look for more complex libraries (probably boost has some). Or you should simply use a reference/shared_ptr as your value_type.

KillianDS
  • 16,936
  • 4
  • 61
  • 70