我最近在程序中出现了一个错误,这让我有些惊讶,但也许不应该,而C (尤其是现代C (提供的大量初始化类型。我有一个看起来像这样的课程:
struct foo
{
foo(const std::set<int> &values) { values_ = values; }
set::set<int> values_;
};
构造foo
s时,通常最容易使用初始化器列表指定内联值集(一个通常在编译时已知的已知值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 }));
这个"起作用"很长时间,但是我的应用程序最近经历了一些晦涩的随机崩溃。调试后,发现以上内容更改为:
auto f = std::make_shared<foo>(std::initializer_list<int>{ 1, 2, 3 });
(也就是说,从复制初始化变为支撑初始化器(解决了问题。这使我提出了一个问题:
是否在上面的示例中显示的初始化列表的复制构造是不安全的练习吗?支撑初始化器中值的寿命是否与完整封闭表达式的寿命不匹配?
这可能只是其他一些仍然存在的错误或某种编译器错误的症状(我正在使用Apple Clang v8.0(。
临时初始化器列表应持续到构造声明结束为止,它们的寿命扩展了它们包装的数组。
虽然make_shared<?>({1,2,3})
不期望工作(请参阅完美转发的缺陷(,但您的其余代码应该。如果您描述的更改实际上会导致代码开始/停止工作,则会有一些内存损坏。
如果这是确定性的,那么问题可能是某人对堆栈上的一个对象有悬空,并且正在修改或阅读它。当您更改构造初始化器列表的方式时,堆栈更改上的对象的布局以及不同的行为发生(某些值是非零或零的,分支的方式不同,或者写的事物不重要在一种情况下,在另一种情况下进行(。
如果它是基于写入的内存损坏,将内存断点放置并跟踪对堆栈的所有访问(尽可能乏味(有时会发现遵循危险指针的位置。