std::map 参数带有空大括号初始值设定项,用于 GCC 中的默认参数段错误

2024-01-07

Problem

我收到了用户的错误报告,报告了我开发的库中的段错误。

错误代码的最小示例是:

#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作为容器的大小,但循环内出现段错误(根本不应该执行)。

解决方法

不过,我可以fix通过将声明更改为以下内容来解决问题:

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 也可以很好地编译和运行代码。 (即使使用与失败的 gcc 4.8.2 相同的 libstdc++ )

Working例子:http://coliru.stacked-crooked.com/a/eb64a7053f542efd http://coliru.stacked-crooked.com/a/eb64a7053f542efd

Question

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

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

我不是在寻找解决方法(因为我知道该怎么做才能使代码工作) 当集成到应用程序中时,有问题的代码在某些计算机上可以正常执行(即使使用 gcc 4.8.2 编译),但在某些计算机上则不能。

Details

我使用以下方法编译它:

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

来自 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 << ...

东盟报道:

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() and end()不一样(begin()是 nullptr) 按照空标准的规定std::map.


看起来像这样bug已在4.8.3/4.9.0中修复 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60367,错误报告有一个类似的例子,还有段错误说:

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

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

foo 的构造函数输出它的地址;我得到以下内容 单次运行的输出: 构造 foo @ 0x7ffff10bdb7f 默认参数位于 0x7ffff10bdb60

它表明只构造了1个foo,并且不在同一地址 作为默认参数。这已经是漫长的一周了,但我不能 看看代码有什么问题。在真实的代码中这是 基于,运行 foo 的析构函数时发生段错误 这是从默认参数移动构造的,因为 底层内存似乎未初始化。

我们可以从一个活生生的例子 http://melpon.org/wandbox/permlink/pHfVahnSgC5tUDLU that 4.9.0没有证明这个问题。

我们可以看到这是有意的功能缺陷报告 994 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#994以及随后的决议N3217 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3217.htm:

本文介绍了相对于当前的详细措辞变化 C++ 工作草案 N3126 实现默认的大括号初始化器 函数的参数,如 N3139“An Incomplete Language Bjarne Stroustrup 的专题”,从而也解决了核心问题 994。

这也包含在提案中N3139:不完整的语言功能 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3139.pdf.

有趣的是注意到Visual Studio 也存在将大括号初始化器作为默认参数的错误 https://stackoverflow.com/q/21044488/1708801我认为这仍然没有解决。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

std::map 参数带有空大括号初始值设定项,用于 GCC 中的默认参数段错误 的相关文章

  • C# AES Rijndael - 检测无效密码

    我正在使用 Rijndael 加密程序中的一些敏感数据 当用户输入错误的密码时 大多数情况下CryptographicException抛出消息 填充无效且无法删除 然而 CryptStream 不会抛出密码错误的异常 而是返回错误解密的流
  • 使用来自本地对象的消息的 std::Exception

    以下代码是否可以安全地抛出带有自定义消息的异常 include
  • 如何指定CMAKE外部项目的编译器?

    我使用ExternalProject Add 包含一个外部项目 我想要的是能够做到 cmake DCMAKE CXX COMPILER
  • HttpResponseMessage 的内容为 JSON

    我有一个 ASP NET MVC WEB API 由于多种原因 由于没有授权而重定向 我不能只使用一个简单的对象并在我的控制器方法中返回它 因此我需要 HttpResponseMessage 类来允许我重定向 目前我正在这样做 var re
  • 如何在C编程中获取当前时间(以毫秒为单位)[重复]

    这个问题在这里已经有答案了 可能的重复 如何使用 ANSI C 测量以毫秒为单位的时间 https stackoverflow com questions 361363 how to measure time in milliseconds
  • ZedGraph 缩放和调整大小

    当我绘制图形 放大和缩小并重新绘制图形时 图形的位置不会改变 我想要做的是 每当重新绘制数据时 视图都会更改以查看所有图形数据 如果您在重绘之前放大或缩小 这似乎会被禁用 Thanks 设置属性 IsZoomOnMouseCenter对于控
  • 无法在更新面板中找到上传的文件

    aspx
  • 将内核链接到 PTX 函数

    我可以使用 PTX 文件中包含的 PTX 函数作为外部设备函数 将其链接到另一个应调用该函数的 cu 文件吗 这是另一个问题CUDA 将内核链接在一起 https stackoverflow com questions 20636800 c
  • 在 C 中声明和初始化数组

    C 有没有办法先声明然后初始化数组 到目前为止 我一直在初始化一个这样的数组 int myArray SIZE 1 2 3 4 但我需要做这样的事情 int myArray SIZE myArray 1 2 3 4 在 C99 中 您可以使
  • 使用成员函数作为 std::shared_ptr 的自定义删除器时出现问题

    我正在尝试弄清楚如何将 std shared ptr 与自定义删除器一起使用 具体来说 我将其与 SDL Surface 一起使用 如下所示 std shared ptr
  • 最小对的总和

    Given 2N点 in a 2D plane 你必须将它们分组为N pairs使得所有对的点之间的距离的总和是最小可能值 所需的输出只是总和 换句话说 如果a1 a2 an分别是第一对 第二对 和第 n 对点之间的距离 则 a1 a2 a
  • 在标准库中静态链接时如何支持动态插件?

    假设一个应用程序myapp exe是使用构建的g 它使用标志 static libstdc 这样就可以安装在没有环境的情况下libstdc so myapp exe还添加了对某些功能的插件支持plugf可以通过动态加载dlopen来自共享库
  • C语言:如何获取使用strtok()一次后剩余的字符串

    我的字符串是 A B C D E 分隔符是 如何获取执行 strtok 一次后剩余的字符串 即 B C D E char a A B C D E char separator char b strtok a separator printf
  • 如何最好地为 Visual Studio 2017 构建的 CMake C++ 项目设置输出目录?

    我使用 Visual Studio 2017 使用 vcxproj 文件构建 C 桌面项目 我喜欢默认行为 其中输出目录是项目下面的子目录 例如 myproj sln myproj vcxproj x64 myproj release my
  • 派生类的聚合初始化

    以下代码无法使用 Visual Studio2017 或在线 GDB 进行编译 我期望它能够编译 因为迭代器只是一个具有类型的类 并且它是从公共继承的 这是不允许的还是在 VS2017 中不起作用 template
  • 检测用户是否正在滚动 dataGridView 滚动条

    我正在更新一个dataGridView与一个新的数据表使用 dataGridView1 DataSource table 但是 我不想在用户滚动 dataGridView 时执行此操作 如何检查滚动条是否正在滚动或已完成滚动 即拖动而不是单
  • C# 记录类型:记录子类之间的相等比较

    给定父记录类型 public record Foo string Value 和两个记录子类Bar and Bee我想知道是否可以实施Equals在基类中 因此 Foo Bar 或 Bee 的实例都被考虑equal基于Value 两者都与E
  • 没有运算符“<<”与这些操作数匹配[重复]

    这个问题在这里已经有答案了 不知道发生了什么事 我查看了与此问题类似的其他帖子 但到目前为止没有解决方案有帮助 这是带有错误部分注释的代码 在某一时刻 它说 不起作用 而在代码的其余部分中 它说 include
  • 如何通过Task.ContinueWith创建传递?

    我想在原始任务结束时添加一个任务 但想保留原始结果和类型 附加任务仅用于记录目的 例如写入控制台等 例如 Task Run gt DateTime Now Hour gt 12 Hey throw new Exception Continu
  • 字符串常量之前应有非限定 ID

    我目前正在编写一个 C 应用程序 它与 math h 结合实现了振荡器 我拥有的代码应该可以很好地用于该应用程序 尝试编译目标文件 但是我遇到编译器错误 很可能与语法 等有关 我认为这与命名空间有关 错误 终端输出 User Name Ma

随机推荐