argumento std :: map com inicializadores de chaves vazios para segfaults de argumento padrão no GCC

Problema

Eu recebi um relatório de erro do usuário relatando um segfault na biblioteca que desenvolvo.

O exemplo mínimo do código defeituoso é:

#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();
}

Quando compilado com o GCC (testei 4.8.2 e 4.7.3), ele imprime corretamente0 como tamanho do c, ontainer, mas segfaults dentro do loop (que não devem ser executados).

Soluções alternativas

No entanto, eu possoconsertar o problema alterando a declaração para:

void f(std::map<std::string, std::string> m = std::map<std::string, std::string>{})

Copiando omap funciona também:

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

Alterando o parâmetro paraconst std::map<...>& também funciona.

O GCC 4.9.1 funciona bem.

Clang também compila e executa o código muito bem. (mesmo ao usar o mesmo libstdc ++ que falha no gcc 4.8.2)

Trabalhando exemplo:http://coliru.stacked-crooked.com/a/eb64a7053f542efd

Pergunta, questão

Definitivamente, o mapa não está em estado válido dentro da função (detalhes abaixo). Parece um bug do GCC (ou libstdc ++), mas quero ter certeza de que não estou cometendo nenhum erro estúpido aqui. É difícil acreditar que esse bug permaneça no gcc por pelo menos duas versões principais.

Então, minha pergunta é: é a maneira de inicializar o padrãostd::map parâmetro errado (e erro no meu código) ou é um erro nostdlibc++ (ougcc)?

Não estou procurando soluções alternativas (como sei o que fazer para que o código funcione) Quando integrado ao aplicativo, o código incorreto executa bem em alguns computadores (mesmo quando compilado com o gcc 4.8.2) em alguns não.

Detalhes

Eu compilo usando:

g++-4.8.2 -g -Wall -Wextra -pedantic  -std=c++11 /tmp/c.cpp -o /tmp/t

Backtrace do 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 é a linha comstd::cout << ...

Relatórios da ASAN:

AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8 

Isso parecenullptr - 8

Valgrind mostra:

==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

Observar o estado interno do mapa mostra que o código realmente precisa falhar:

std::map::begin() no libstdc ++ retorna o valor de

this->_M_impl._M_header._M_parent

da sua representação interna,std::map::end() retorna:

&this->_M_impl._M_header

gdb mostra:

(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

Então, valor debegin() eend() não são os mesmos (begin() é nullptr) conforme exigido pelo padrão para vaziosstd::map.

questionAnswers(1)

yourAnswerToTheQuestion