v15*_*4c1 33 c++ gcc default-arguments c++11
我收到了用户报告我开发的库中的段错误的错误报告.
错误代码的最小示例是:
#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();
}
Run Code Online (Sandbox Code Playgroud)
当使用GCC编译时(我测试了4.8.2和4.7.3),它正确地打印0为容器的大小,但是循环内部的段错误(根本不应该执行).
不过,我可以修复通过改变声明的问题:
void f(std::map<std::string, std::string> m = std::map<std::string, std::string>{})
Run Code Online (Sandbox Code Playgroud)
复制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";
}
}
Run Code Online (Sandbox Code Playgroud)
更改参数const std::map<...>&也有效.
GCC 4.9.1工作正常.
Clang也编译并运行代码就好了.(即使使用与失败的gcc 4.8.2相同的libstdc ++)
工作示例:http://coliru.stacked-crooked.com/a/eb64a7053f542efd
地图在函数内部肯定不是有效状态(详见下文).它看起来像一个GCC(或libstdc ++)错误,但我想确定我在这里没有犯一些愚蠢的错误.很难相信这样的bug会在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
Run Code Online (Sandbox Code Playgroud)
从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
Run Code Online (Sandbox Code Playgroud)
/tmp/c.cpp:9是该行 std::cout << ...
ASAN报道:
AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8
Run Code Online (Sandbox Code Playgroud)
这似乎是 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
Run Code Online (Sandbox Code Playgroud)
查看地图的内部状态表明代码确实必须失败:
std::map::begin() 在libstdc ++中返回值
this->_M_impl._M_header._M_parent
Run Code Online (Sandbox Code Playgroud)
从它的内部表示,std::map::end()返回:
&this->_M_impl._M_header
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
因此,空的标准规定的值begin()和end()不一样(begin()是nullptr)std::map.
Sha*_*our 22
看起来这个bug在4.8.3/4.9.0中修复了,bug报告有一个类似的例子,而且seg-faults说:
附加的最小测试用例具有以下函数,默认构造的默认参数:
Run Code Online (Sandbox Code Playgroud)void do_something( foo f = {} ) { std::cout << "default argument is at " << &f << std::endl; }foo的构造函数输出其地址; 我从一次运行得到以下输出:构造foo @ 0x7ffff10bdb7f默认参数是0x7ffff10bdb60
它表明只构造了1个foo,并且与默认参数的地址不同.这是一个漫长的一周,但我看不出代码有什么问题.在它所基于的实际代码中,运行从默认参数移动构造的foo的析构函数时发生了段错误,因为底层内存似乎未初始化.
我们可以从一个看到活生生的例子是4.9.0不能证明这个问题.
我们可以看到这是故障报告994和随后的N3217决议的故意功能:
本文提供了与当前C++工作草案N3126相关的详细措辞更改,以实现函数默认参数的大括号初始化器,如Bjarne Stroustrup在N3139"An Incomplete Language Feature"中所提出的,从而也解决了核心问题994.
N3139:不完整语言功能提案中也包含了这一点.
有趣的是,Visual Studio也有一个关于大括号初始化程序的错误作为默认参数,我认为仍然没有解决.
| 归档时间: |
|
| 查看次数: |
945 次 |
| 最近记录: |