我最近在我的程序中出现了一个让我感到惊讶的错误,但也许它不应该是C ++(特别是现代C ++)提供的大量初始化类型。我有一个看起来像这样的课程:
struct foo
{
foo(const std::set<int> &values) { values_ = values; }
set::set<int> values_;
};
构造foo
时,通常最容易使用初始化列表指定内联值(一个实例通常会有2-3个在编译时已知的已知值),如这样:
auto f = std::make_shared<foo>({ 1, 2, 3 });
但是,我记得当我第一次写课foo
时,上面的用法给了我编译时的问题;我不记得确切的错误,但我发现显式调用initializer_list
构造函数允许编译器正确推断如何处理参数,如下所示:
auto f = std::make_shared<foo>(std::initializer_list<int>({ 1, 2, 3 }));
这&#34;工作&#34;很长一段时间,但我的应用程序最近经历了一些模糊,随机的崩溃。调试后,发现将上述内容改为:
auto f = std::make_shared<foo>(std::initializer_list<int>{ 1, 2, 3 });
(即,从复制初始化更改为支持的初始化程序)修复了问题。这让我想到了这个问题:
如上例所示,初始化程序列表的复制构造是不安全的做法吗?支撑初始值设定项中值的生命周期是否与完整封闭表达式的生命周期不匹配?
这可能只是其他一些仍存在的错误或某种编译器错误的症状(我使用的是Apple clang v8.0)。
答案 0 :(得分:1)
临时初始化列表应该持续到它们构造语句的结尾,并且它们将终生扩展它们包装的数组。
虽然预计make_shared<?>({1,2,3})
不起作用(请参阅完美转发的不完善之处),但其余代码应该如此。如果您描述的更改实际导致代码开始/停止工作,则会导致内存损坏。
如果这是确定性的概率很高,那么问题可能是有人对堆栈中的对象有悬空引用,并且正在修改它或读取它。当您更改构造初始化程序列表的方式时,堆栈上对象的布局会发生变化,并且会发生不同的行为(某些值为非零或零,并且分支的行为方式不同,或者正在写入的内容不会发生变化。在一个案例中,在另一个案件中,并在另一个案件中。)
如果它是基于写入的内存损坏,那么放置内存断点并跟踪对堆栈区域的所有访问(尽可能繁琐)有时可以找到跟踪指针的位置。