mardi 25 avril 2017

Is this unsafe usage of a braced initializer list?

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

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 unexisting bug, or a compiler bug of some sort (I'm using Apple clang v8.0).

Aucun commentaire:

Enregistrer un commentaire