Problema
Recibí un informe de error del usuario que informa una falla de segmento en la biblioteca que desarrollo.
El ejemplo mínimo del código defectuoso es:
#include <map>
#include <string>
#include <iostream>
void f(std::map<std::string, std::string> m = {})
{
std::cout << m.size() << "\n";
for (const auto& s: m) {
std::cout << s.first << "->" << s.second <<"\n";
}
}
int main()
{
f();
}
Cuando se compila con GCC (probé 4.8.2 y 4.7.3) se imprime correctamente 0
como tamaño del contenedor, pero fallas de segmento dentro del ciclo (que no debería ejecutarse en absoluto).
Soluciones alternativas
Sin embargo, puedo arreglar el problema cambiando la declaración a:
void f(std::map<std::string, std::string> m = std::map<std::string, std::string>{})
copiando el map
funciona también:
void f(std::map<std::string, std::string> mx = {})
{
auto m = mx;
std::cout << m.size() << "\n";
for (const auto& s: m) {
std::cout << s.first << "->" << s.second <<"\n";
}
}
Cambiar el parámetro a const std::map<...>&
también funciona
GCC 4.9.1 funciona bien.
Clang también compila y ejecuta el código correctamente. (incluso cuando se usa la misma libstdc++ que falla en gcc 4.8.2)
Laboral ejemplo: http://coliru.stacked-crooked.com/a/eb64a7053f542efd
Pregunta
El mapa definitivamente no está en un estado válido dentro de la función (detalles a continuación). Parece un error de GCC (o libstdc++), pero quiero estar seguro de que no estoy cometiendo un error estúpido aquí. Es difícil creer que un error así permanezca en gcc durante al menos 2 versiones principales.
Entonces mi pregunta es: ¿Es la forma de inicializar por defecto std::map
parámetro incorrecto (y error en mi código) o es un error en stdlibc++
(o gcc
)?
No estoy buscando soluciones alternativas (ya que sé qué hacer para que el código funcione) Cuando está integrado en la aplicación, el código infractor se ejecuta bien en algunas computadoras (incluso cuando se compila con gcc 4.8.2) en otras no.
Detalles
Lo compilo usando:
g++-4.8.2 -g -Wall -Wextra -pedantic -std=c++11 /tmp/c.cpp -o /tmp/t
Seguimiento desde gdb:
#0 std::operator<< <char, std::char_traits<char>, std::allocator<char> > (__os=..., __str=...) at /usr/src/debug/sys-devel/gcc-4.8.2/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:2758
#1 0x0000000000400f36 in f (m=std::map with 0 elements) at /tmp/c.cpp:9
#2 0x0000000000400fe0 in main () at /tmp/c.cpp:15
/tmp/c.cpp:9 es la línea con std::cout << ...
ASAN informa:
AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8
esto parece nullptr - 8
valgrind muestra:
==28183== Invalid read of size 8
==28183== at 0x4ECC863: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.2/libstdc++.so.6.0.18)
==28183== by 0x400BD5: f(std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >) (c.cpp:9)
==28183== by 0x400C7F: main (c.cpp:15)
==28183== Address 0xffffffffffffffe8 is not stack'd, malloc'd or (recently) free'd
Mirar el estado interno del mapa muestra que el código realmente tiene que fallar:
std::map::begin()
en libstdc++ devuelve el valor de
this->_M_impl._M_header._M_parent
de su representación interna, std::map::end()
devoluciones:
&this->_M_impl._M_header
gdb muestra:
(gdb) print m._M_t._M_impl._M_header
$5 = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffd6d8, _M_right = 0x7fffffffd6d8}
(gdb) print &m._M_t._M_impl._M_header
$6 = (std::_Rb_tree_node_base *) 0x7fffffffd6a8
Entonces valor de begin()
y end()
no son lo mismo (begin()
es nullptr) según lo exige el estándar para vacío std::map
.
FWIW, cuando reemplacé
std::cout << s.first << "->" << s.second <<"\n";
porstd::cout << "Came here\n";
el programa imprime la línea una vez y luego se cuelga.– R Sahu
3 de marzo de 2015 a las 16:51
“Es difícil creer que un error así permanezca en gcc durante al menos 2 versiones principales”. – Debes ser nuevo aquí
–MM
03/03/2015 a las 21:55