Edit: This is indeed caused by a bug in Visual Studio - and it has already been fixed. The issue is not reproducible after applying Update 2 to Visual Studio (release candidate available here). I apologize; I thought I was up to date with my patches.
I can't for the life of me figure out why I get a seg fault when I run the following code in Visual Studio 2013:
#include <initializer_list>
#include <memory>
struct Base
{
virtual int GetValue() { return 0; }
};
struct Derived1 : public Base
{
int GetValue() override { return 1; }
};
struct Derived2 : public Base
{
int GetValue() override { return 2; }
};
int main()
{
std::initializer_list< std::shared_ptr<Base> > foo
{
std::make_shared<Derived1>(),
std::make_shared<Derived2>()
};
auto iter = std::begin(foo);
(*iter)->GetValue(); // access violation
return 0;
}
I was expecting the initializer_list
to take ownership of the created shared_ptr
s, keeping them in scope until the end of main
.
Oddly enough, if I try to access the second item in the list, I get the expected behavior. For example:
auto iter = std::begin(foo) + 1;
(*iter)->GetValue(); // returns 2
Considering these things, I'm guessing this may be a bug in the compiler - but I wanted to make sure I wasn't overlooking some explanation for why this behavior might be expected (e.g., maybe in how rvalues are handled in initializer_list
s).
Is this behavior reproducible in other compilers, or can someone explain what might be happening?
The
shared_ptr
objects returned frommake_shared
are temporaries. They will be destroyed at the end of the full-expression, after being used to initializeshared_ptr<Base>
instances.But ownership of the user objects (the
Derived1
andDerived2
) should be shared (or "transferred" if you like) to theshared_ptr
instances in the list. Those user objects should live until the end ofmain
.I just ran the code from your question using Visual Studio 2013 and got no access violation. Oddly, when I trace to
main()
and~Base()
, I get the following output:That does look wrong.
And if I do something with the return value of
GetValue()
, it is wrong (0
instead of1
) and I get the access violation. It occurs after all tracing output, however. And it seems somewhat intermittent.Here's the final version of the code I'm working with:
Stepping through shows that destructors are called immediately after initializer list construction for
shared_ptr<Derived1>
(correct, its object has been moved to ashared_ptr<Base>
), and the matchingshared_ptr<Base>
, which is very very wrong.See the original answer for analysis of object lifetimes of the code in the question. This one isolates the bug.
I made a minimal reproduction. It's more code, but a lot less library code involved. And easier to trace.
The output is BAD on both x64:
and x86:
Definitely a compiler bug, and a pretty severe one. If you file a report on Connect I and many others will be happy to upvote.