¿Se garantiza que for(auto i : unordered_map) tenga el mismo orden cada vez?

3 minutos de lectura

Cuando itero sobre un std::unordered_map con el rango basado en el ciclo for dos veces, ¿se garantiza que el orden sea igual?

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 += ");"

En el ejemplo anterior, la cadena resultante debe tener ese formato. Por lo tanto, es importante que en ambas ocasiones el orden de iteración sea el mismo.

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

¿Está esto garantizado en C++?

  • Dígame que está protegiendo contra la inyección de SQL en alguna parte.

    – D. Shawley

    18 de agosto de 2013 a las 16:40

  • @D.Shawley No lo hago en este momento. Pero se trata de guardados de un juego de computadora. Codifico el juego y no hay forma de inyectar SQL desde fuera del código de la aplicación.

    – danijar

    18 de agosto de 2013 a las 16:44

  • Simplemente me estremezco cada vez que veo la construcción de cadenas SQL mediante la concatenación de cadenas sin procesar.

    – D. Shawley

    18/08/2013 a las 19:50

  • @D.Shawley Eso es totalmente comprensible en un contexto donde las solicitudes a través de redes pueden provocar accesos a la base de datos.

    – danijar

    18/08/2013 a las 21:55

  • No siempre son datos de una fuente no confiable. He tenido aplicaciones que fallan debido a cosas como establecer el nombre del jugador en cosas que no son seguras para SQL.

    – D. Shawley

    19 de agosto de 2013 a las 1:58

El orden de iteración de los contenedores asociativos no ordenados solo puede cambiar cuando se repite como resultado de una operación de mutación (como se describe en C++11 23.2.5/8). No está modificando el contenedor entre iteraciones, por lo que el orden no cambiará.

Aunque la especificación no establece explícitamente que la repetición no puede ocurrir en ningún otro momento, hacerlo invalidaría todos los iteradores sobre el contenedor, haciendo que cualquier iteración sea imposible.

avatar de usuario
Karthik T

¿Por qué no construirlos juntos?

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 ")";

Se ve mejor también en mi opinión.

Tenga en cuenta que si esta sección es crítica para el rendimiento, podría considerar optimizar el proceso de creación de cadenas con std::stringstream como se muestra aquí, aunque no está claro cuánto podría ayudar.

  • Considere usar ostringstream en cambio, pero este es el mejor enfoque.

    – D. Shawley

    18 de agosto de 2013 a las 16:39

  • @D.Shawley sí, probablemente si esta es una sección de alto rendimiento. De lo contrario, esto podría ser mejor simplemente por motivos de legibilidad, de cualquier manera, me estaba enfocando principalmente en la solución de su problema.

    – Karthik T.

    18 de agosto de 2013 a las 16:41

  • ¡Excelente! Incluso podría reducir eso, porque se da la primera clave y valor. std::string keys = "id", values = to_string(Id); for(auto i : serialized) keys += ", " + i.first, values += ", " + i.second;.

    – danijar

    18/08/2013 a las 16:50

¿Ha sido útil esta solución?