Is this unsafe usage of a braced initializer list?

被刻印的时光 ゝ 提交于 2019-12-08 03:08:07

问题


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_;
};

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 });

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?

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).


回答1:


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

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.



来源:https://stackoverflow.com/questions/43609392/is-this-unsafe-usage-of-a-braced-initializer-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!