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
The shared_ptr objects returned from make_shared are temporaries. They will be destroyed at the end of the full-expression, after being used to initialize shared_ptr instances.
But ownership of the user objects (the Derived1 and Derived2) should be shared (or "transferred" if you like) to the shared_ptr instances in the list. Those user objects should live until the end of main.
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:
C:\Code\SO22924358>cl /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
C:\Code\SO22924358>main
~Base()
Reached end of main
~Base()
That does look wrong.
And if I do something with the return value of GetValue(), it is wrong (0 instead of 1) and I get the access violation. It occurs after all tracing output, however. And it seems somewhat intermittent.
C:\Code\SO22924358>cl /Zi /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
/debug
main.obj
C:\Code\SO22924358>main
~Base()
GetValue() returns 0
Reached end of main
~Base()
Here's the final version of the code I'm working with:
#include
#include
#include
struct Base
{
virtual int GetValue() { return 0; }
~Base() { std::cerr << "~Base()" << std::endl; }
};
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 > foo
{
std::make_shared(),
std::make_shared()
};
auto iter = std::begin(foo);
std::cerr << "GetValue() returns " << (*iter)->GetValue() << std::endl; // access violation
std::cerr << "Reached end of main" << std::endl;
return 0;
}
Stepping through shows that destructors are called immediately after initializer list construction for shared_ptr (correct, its object has been moved to a shared_ptr), and the matching shared_ptr, which is very very wrong.