2015-03-03 2 views
33

Проблемы

Я получил сообщение об ошибке от пользователя сообщающего Segfault в библиотеке я развиваться.станда :: Карта аргумент с пустыми скрепляющими-инициализаторами для аргументов по умолчанию в GCC ошибки сегментации

Минимальный пример неисправного кода:

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

После компиляции с GCC (я проверил 4.8.2 и 4.7.3) он правильно печатает 0 как размер контейнера, но внутри возвращает ошибку сегментации петли (который не должен выполняться вообще).

Обходные

Однако, я могу исправить проблему путем изменения декларации:

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

Копирование map работ, а также:

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

Изменение параметра в const std::map<...>& также работает.

GCC 4.9.1 работает нормально.

Clang также компилирует и запускает код просто отлично. (Даже при использовании той же libstdC++ как неудачу GCC 4.8.2)

Работа пример: http://coliru.stacked-crooked.com/a/eb64a7053f542efd

Вопрос

карта, безусловно, не в допустимом состоянии внутри функции (подробности ржали). Это похоже на ошибку GCC (или libstdC++), но я хочу быть уверенным, что я не делаю какую-то глупую ошибку. Трудно поверить, что такая ошибка останется в gcc по меньшей мере для 2 основных версий.

Так что мой вопрос: Является ли способ инициализации по умолчанию std::map параметра неправильно (и ошибки в моем коде), или это ошибка в stdlibc++ (или gcc)?

Я не ищу обходные пути (как я знаю, что делать, чтобы сделать работу с кодом) При интегрировании в приложение код нарушения выполняется на некоторых компьютерах (даже при компиляции с gcc 4.8.2) на некоторые нет.

Подробности

компилировать его с помощью:

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

Backtrace из БГД:

#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/с.каст: 9 является линия с std::cout << ...

докладов Асан:

AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8 

Это похоже nullptr - 8

Valgrind показывает:

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

Глядя на внутреннее состояние карты показывает, что код действительно должен потерпеть неудачу:

std::map::begin() в libstdC++ возвращает значение

this->_M_impl._M_header._M_parent 

от его внутреннее представление, std::map::end() возвращается:

&this->_M_impl._M_header 

GDB показывает:

(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 

Так значение begin() и end() не то же самое (begin() nullptr), как это предусмотрено стандартом для пустого std::map.

+0

FWIW, Когда я заменил 'std :: cout << s.first << "->" << s.second << "\ n"; 'by' std :: cout << "Пришел здесь \ n"; ', программа печатает строку один раз, а затем зависает. –

+4

«Трудно поверить, что такая ошибка останется в gcc по меньшей мере для 2-х основных версий». - Вы должны быть новыми здесь –

ответ

22

Похоже, этот bug was fixed in 4.8.3/4.9.0, отчет об ошибке, который имеет аналогичный пример и также SEG-ошибку говорит:

Прилагаемый минимален TestCase имеет следующую функцию с по умолчанию возведенного аргумента по умолчанию:

void do_something(foo f = {}) 
{  std::cout << "default argument is at " << &f << std::endl; 
} 

Конструктор для foo выводит свой адрес; Я получил следующий вывод один проход: построен Foo @ 0x7ffff10bdb7f по умолчанию аргумент в 0x7ffff10bdb60

Это показывает, что только 1 Foo был построен, а не по тому же адресу, что и аргумента по умолчанию. Это была недельная неделя, но я не могу видеть что-то не так с кодом. В реальном коде, на котором это было , возникла segfault при запуске деструктора foo , который был перемещен по ходу из аргумента по умолчанию, поскольку базовая память была, по-видимому, неинициализирована.

Мы можем видеть из live example, что 4.9.0 не демонстрирует эту проблему.

Мы можем видеть, что это намеренное функциональность от defect report 994 и последующего разрешение N3217:

В данной статье представлена ​​подробные формулировки изменений относительно текущего C++ Рабочего проекта N3126 для реализации фигурных скобок-инициализаторов по умолчанию аргументов функции, как предложено в N3139 «Неполный язык Функция» от Bjarne Stroustrup, тем самым также рассматривая основной вопрос 994.

Это также рассматривается в предложении N3139: An Incomplete Language Feature.

Интересно отметить, что Visual Studio also has a bug with respect to brace-initializers as default arguments, который, как я думаю, до сих пор не разрешен.

+1

Спасибо! Это именно то, что я хотел знать и мог Не находите. – v154c1

Смежные вопросы