4

If I do this:

#include <map>
#include <iostream>

int main()
{
    std::multimap<char, int> m;

    m.emplace('a', 100);
    m.emplace('b', 200);
    m.emplace('b', 201);
    m.emplace('c', 300);

    for (const auto& p : m)
        std::cout << p.first << '\t' << p.second << '\n';
}

… then, since C++11, I'm guaranteed that the element with value 200 will precede the element with value 201.

But what if I do this?

#include <map>
#include <iostream>

int main()
{
    std::multimap<char, int> m{
       {'a', 100},
       {'b', 200},
       {'b', 201},
       {'c', 300}
    };

    for (const auto& p : m)
        std::cout << p.first << '\t' << p.second << '\n';
}

Are we guaranteed that "insertion order" matches the order of elements in the initialiser?

A quick test gives encouraging results, but doesn't really prove anything.

I'm writing C++17.

(I could switch to std::map with a compound key, but I have thousands of these things managed by a common interface, and only a few contain duplicate keys so I'm hesitant to introduce that complexity overall.)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055

1 Answers1

4

[associative.reqmts]:

... i and j satisfy input iterator requirements and refer to elements implicitly convertible to value_type, [i, j) denotes a valid range ... il designates an object of type initializer_list<value_type> ...

Expression    Assertion/note
              pre-/post-condition

X(i,j,c)      Effects: Constructs an empty container
              and inserts elements from the range [i, j)
              into it; uses c as a comparison object.

X(i,j)        Effects: Same as above, but
              uses Compare() as a
              comparison object.

X(il)         same as X(il.begin(), il.end())

So, the effects being the same, ordering guarantees are same when constructing from an initialiser list, as they are when inserting a range of iterators.

Being input iterators, the range cannot in general be iterated out of order.

Barry
  • 286,269
  • 29
  • 621
  • 977
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    The pedantic side of me wants to say that `inserts elements from the range [i, j)` only specifies the bounds of the range, not the order in which it has to be iterated. – lcs Jan 30 '19 at 17:59
  • @Ics Very pedantically yeah, the order is unspecified. That appears to apply to pretty much all operations that insert from a range of iterators into any container. It would be pretty infuriating if for example `vector.insert(first, last)` didn't insert in order. – eerorika Jan 30 '19 at 18:04
  • I would expect `[i,j)` to be defined to include an order property somewhere in the text, or, more likely, in a normative reference to some obscure mathsy standard – Lightness Races in Orbit Jan 30 '19 at 18:07
  • 1
    @LightnessRacesinOrbit at least, the iterator category is only requried to be input iterator, which doens't provide any way of iterating non-linearly. But I haven't found an explicit rule saying that it couldn't do silly things with forward or more able iterators. – eerorika Jan 30 '19 at 18:09
  • Found it in ISO 80000-2:2009 section 6, which [C++ refers to normatively](http://eel.is/c++draft/intro.refs#1.10), and it is _not_ defined as an n-tuple but as a set, so order isn't technically well-defined. Dafuq – Lightness Races in Orbit Jan 30 '19 at 18:15
  • Yeah, even with forward iterators you could do even indices first, then odd. Doubtful anyone *would* though – lcs Jan 30 '19 at 18:46