大括号初始化器列表的这种不安全用法吗? [英] Is this unsafe usage of a braced initializer list?

查看:77
本文介绍了大括号初始化器列表的这种不安全用法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我的程序中出现了一个错误,这让我有些惊讶,但也许不应该这样,因为C ++(尤其是现代C ++)提供了多种初始化类型。我有一个看起来像这样的类:

I had a bug crop up in my program recently that surprised me a bit, but perhaps it shouldn't, with the vast number of types of initialization that C++ (especially modern C++) provides. I have a class that looks something like this:

struct foo
{
    foo(const std::set<int> &values) { values_ = values; }

    set::set<int> values_;
};

构造 foo s时,通常最简单使用初始化程序列表内联指定一组值(一个实例通常具有2-3个在编译时已知的已知值),例如:

When constructing foos, it's usually easiest to specify the set of values inline using an initializer list (an instance will typically have 2-3 known values that are known at compile time), like this:

auto f = std::make_shared<foo>({ 1, 2, 3 });

但是,我记得当我第一次写类 foo ,上面的用法给了我编译时的问题;我不记得确切的错误,但是我发现显式调用 initializer_list 构造函数可以使编译器正确推断出如何处理参数,例如:

However, I recall that when I was first writing class foo, the above usage gave me compile-time issues; I don't recall the exact error, but I found that explicitly invoking the initializer_list constructor allowed the compiler to properly deduce how to handle the arguments, like this:

auto f = std::make_shared<foo>(std::initializer_list<int>({ 1, 2, 3 }));

这个工作了很长时间,但是我的应用程序遇到了一些晦涩,看起来像随机的崩溃最近。调试之后,发现将上面的内容更改为:

This "worked" for a long time, but my application was experiencing some obscure, random-looking crashes recently. After debugging, it was found that changing the above to:

auto f = std::make_shared<foo>(std::initializer_list<int>{ 1, 2, 3 });

(即,从复制初始化更改为括号初始化器)解决了该问题。这使我想到了一个问题:

(that is, changing from copy initialization to a braced initializer) fixed the problem. Which brings me to the question:


是否如上面示例中所示构造初始化列表的副本是不安全的做法?括号初始化程序中的值的生存期与完全封闭的表达式的生存期不匹配吗?

Is copy construction of an initializer list as shown in the example above an unsafe practice? Does the lifetime of the values in the braced initializer not match the lifetime of the full enclosed expression?

这可能只是一个其他仍然存在的错误或某种编译器错误的症状(我使用的是Apple clang v8.0)。

It's possible that this was just a symptom of some other, still-existing bug, or a compiler bug of some sort (I'm using Apple clang v8.0).

推荐答案

临时初始化器列表应持续到构造它们的语句结束时为止,并且它们的生命周期将扩展它们包装的数组。

Temporary initializer lists should last until the end of the statement they are constructed, and they lifetime extend the array they wrap.

make_shared<?(<?>({1,2,3})不能正常工作(请参阅完美转发的不完善之处),其余的代码也应如此。如果您描述的更改实际上导致代码开始/停止工作,则说明您的内存损坏。

While make_shared<?>({1,2,3}) is not expected to work (see the imperfections of perfect forwarding), the rest of your code should. If the change you describe actually causes the code to start/stop working, you have some memory corruption.

如果这是确定性的,则可能是有人对堆栈上的对象有一个悬空的引用,并且正在对其进行修改或读取。当您更改构造初始化器列表的方式时,堆栈上对象的布局会更改,并且会发生不同的行为(某些值非零或零,并且分支采用不同的方式,或者写入的内容无关紧要

If this is deterministic with high probability, the problem is likely that someone has a dangling reference to the an object on the stack, and is modifying it or reading it. When you change how you construct the initializer list, the layout of objects on the stack change, and different behaviour happens (some value is non-zero or zero and a branch goes a different way, or the thing being written to doesn't matter in one case and does in another).

如果这是基于写的内存损坏,则放置一个内存断点并跟踪对堆栈该区域的所有访问(如可能会很乏味)有时可以发现正在跟随单向指针的位置。

If it is a write-based memory corruption, putting a memory breakpoint and tracking all access to that area of the stack (as tedious as it can be) can sometimes spot the location where the danging pointer is being followed.

这篇关于大括号初始化器列表的这种不安全用法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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