34

When I iterate over a std::unordered_map with the range based for loop twice, is the order guaranteed to be equal?

std::unordered_map<std::string, std::string> map;

std::string query = "INSERT INTO table (";
bool first = true;
for(auto i : map)
{
    if(first) first = false;
    else query += ", ";
    query += i.first;
}
query += ") ";

query += "VALUES (";
first = true;
for(auto i : map)
{
    if(first) first = false;
    else query += ", ";
    query += i.second;
}
query += ");"

In the example above, the resulting string should be in that form. Therefore, it is important that both times, the order of iteration is the same.

INSERT INTO table (key1, key2, key3) VALUES (value1, value2, value3);

Is this guaranteed in C++?

danijar
  • 32,406
  • 45
  • 166
  • 297
  • Please tell me that you are protecting against SQL injection somewhere. – D.Shawley Aug 18 '13 at 16:40
  • @D.Shawley I don't at the moment. But it is about saves of a computer game. I code the game and there is no way to inject SQL from outside of the application code at all. – danijar Aug 18 '13 at 16:44
  • 1
    I just cringe any time that I see building SQL strings by raw string concatenation. – D.Shawley Aug 18 '13 at 19:50
  • @D.Shawley That is totally understandable in a context where requests over networks can cause database accesses. – danijar Aug 18 '13 at 21:55
  • 5
    It's not always data from a untrusted source. I've had applications crash based on things like setting the player's name to stuff that isn't SQL safe. – D.Shawley Aug 19 '13 at 01:58
  • @danijar Do it anyway. You don't want "INSERT BIG COMPANY NAME HERE HACKED BECAUSE DIDN'T THINK DATABASE WARRANTED SQL PROTECTION" – noɥʇʎԀʎzɐɹƆ Dec 15 '15 at 02:37

2 Answers2

40

The iteration order of unordered associative containers can only change when rehashing as a result of a mutating operation (as described in C++11 23.2.5/8). You are not modifying the container between iterations, so the order will not change.

Although the specification doesn't explicitly state that rehashing can't occur at any other time, doing so would invalidate all iterators over the container, making any iteration at all impossible.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
17

Why not build them together?

for(auto i : map)
{
    if(first) first = false;
    else{
        keys += ", ";
        query += ", ";
    }
    keys += i.first;

    values += i.second;
}

std::string query = "INSERT INTO table (" + keys + ") VALUES (" + values ")";

Looks nicer too imo.

Please note, if this section is performance critical, you could consider optimizing the string building process with std::stringstream as shown here, though it is not clear how much that might help

Community
  • 1
  • 1
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • 1
    Consider using `ostringstream` instead, but this is the better approach. – D.Shawley Aug 18 '13 at 16:39
  • @D.Shawley yes probably if this is a performance intensive section. Else this might be better purely on readability grounds, either way I was mainly focusing on the fix to his problem. – Karthik T Aug 18 '13 at 16:41
  • Great! I could even cut that down, because the first key and value is given. `std::string keys = "id", values = to_string(Id); for(auto i : serialized) keys += ", " + i.first, values += ", " + i.second;`. – danijar Aug 18 '13 at 16:50