std :: map参数与空的大括号初始化器在GCC中的默认参数segfaults [英] std::map argument with empty brace-initializers for default argument segfaults in GCC

查看:532
本文介绍了std :: map参数与空的大括号初始化器在GCC中的默认参数segfaults的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我收到来自用户报告我开发的图书馆中的segfault的错误报告。

I got a bug report from user reporting a segfault in library I develop.

错误代码的最小示例为:

The minimal example of the faulty code is:

#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 ,但在循环内部执行segfaults(根本不应该执行)。

When compiled with GCC (I tested 4.8.2 and 4.7.3) it correctly prints 0 as size of the container, but segfaults inside the loop (which shouldn't be executed at all).

但是,我可以通过将声明更改为修复问题:

However, I can fix the problem by changing the declaration to:

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

复制映射

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< ...>& 也可以使用。

Changing the parameter to const std::map<...>& also works.

GCC 4.9.1可以正常工作。

GCC 4.9.1 works fine.

Clang也编译并运行代码很好。 (即使使用相同的libstdc ++作为失败的gcc 4.8.2)

Clang also compiles and runs the code just fine. (even when using the same libstdc++ as failing gcc 4.8.2)

工作示例:http://coliru.stacked-crooked.com/a/eb64a7053f542efd

地图绝对不是在函数内部的有效状态(细节下面)。
它看起来像一个GCC(或libstdc ++)错误,但我想确保我不会在这里做一些愚蠢的错误。
很难相信这样的错误会留在gcc至少2个主要版本。

The map is definitely not in valid state inside the function (details bellow). It looks like a GCC (or libstdc++) bug, but I want to be sure I'm not making some stupid mistake here. It's hard to believe such a bug would stay in gcc for at least 2 major version.

所以我的问题是:初始化默认值的方式 std :: map 参数错误(和我的代码中的错误)或者是 stdlibc ++ (或 gcc )?

So my question is: Is the way of initializing default std::map parameter wrong (and bug in my code) or is it a bug in stdlibc++ (or gcc)?

我不是寻找解决方法(因为我知道如何做到代码工作)
当集成在应用程序中时,有些计算机上的违规代码执行得很好(即使使用gcc 4.8.2编译时)。

I'm not looking for workarounds (as I know what to do to make to code work) When integrated in the application, the offending code executes fine on some computers (even when compiled with gcc 4.8.2) on some doesn't.

我编译它使用:

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

Backtrace from gdb:

Backtrace from 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是 std :: cout<< ...

/tmp/c.cpp:9 is the line with std::cout << ...

ASAN报告:

AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8 

看起来像 nullptr - 8

valgrind显示:

valgrind shows:

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

查看地图的内部状态显示代码真的必须失败:

Looking at internal state of the map shows that the code really has to fail:

std :: map :: begin()在libstdc ++中返回

std::map::begin() in libstdc++ returns value of

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()对于空 std :: map 的标准,相同的( begin()是nullptr)。

So value of begin() and end() are not the same (begin() is nullptr) as mandated by standard for empty std::map.

推荐答案

看起来像这 bug修复在4.8.3 / 4.9.0 ,bug报告有一个类似的例子和seg-faults说:

Looks like this bug was fixed in 4.8.3/4.9.0, the bug report which has a similar example and also seg-faults says:


附加的最小测试用例具有以下函数,
默认构造的默认参数:

The attached minimal testcase has the following function with default-constructed default argument:

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

foo的构造函数输出其地址;我从一次运行得到以下
输出:
构造foo @ 0x7ffff10bdb7f
默认参数在0x7ffff10bdb60

The constructor for foo outputs its address; I got the following output from a single run: constructed foo @ 0x7ffff10bdb7f default argument is at 0x7ffff10bdb60

它显示只有1 foo被构造,并且不在与默认参数相同的地址
。这是一个loooong周,但我不能
看到任何错误的代码。在基于
的实际代码中,当运行从默认参数移动构建的foo
的析构函数时,会发生segfault,因为
底层内存似乎未初始化。

It shows that only 1 foo was constructed, and not at the same address as that of the default argument. It's been a loooong week, but I can't see anything wrong with the code. In the real code on which this was based, a segfault was occurring when running the destructor of a foo that was move-constructed from the default argument, because the underlying memory was seemingly uninitialised.

我们可以从活动示例 4.9.0 不会显示此问题。

We can see from a live example that 4.9.0 does not demonstrate this problem.

请参阅缺陷报告994 的故意功能及其后的决议 N3217 : / p>

We can see this was intentional functionality from defect report 994 and the subsequent resolution N3217:


本文介绍了相对于当前的
C ++ Working Draft N3126的详细措辞更改,以实现默认的括号初始化
函数的参数,如Bjarne Stroustrup的N3139An Incomplete Language
Feature中提出的,因此也解决了核心问题994。

This paper presents detailed wording changes relative to the current C++ Working Draft N3126 to implement brace-initializers for default arguments for functions, as proposed in N3139 "An Incomplete Language Feature" by Bjarne Stroustrup, thereby also addressing core issue 994.

这也在提案 N3139:不完整的语言功能

有趣的是, Visual Studio也有一个bug作为默认参数,我认为尚未解决。

Interesting to note that Visual Studio also has a bug with respect to brace-initializers as default arguments which I think is still unresolved.

这篇关于std :: map参数与空的大括号初始化器在GCC中的默认参数segfaults的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆