Reputation: 531
I was writting a file manager and saw reproducible crashes when I open a folder twice. To mininize the related code:
#include <vector>
#include <memory>
#include <boost/smart_ptr.hpp>
namespace myns {
using std::shared_ptr;
}
class Base {
public:
virtual ~Base() {}
};
class Derived_1 : public Base {
public:
~Derived_1() {} // breakpoint 1
};
myns::shared_ptr<Derived_1> Derived_1_ptr() {
static const myns::shared_ptr<Derived_1> r{new Derived_1};
return r;
}
class Derived_2 : public Base {};
myns::shared_ptr<Derived_2> Derived_2_ptr() {
static const myns::shared_ptr<Derived_2> r{new Derived_2};
return r;
}
std::vector<myns::shared_ptr<Base>> all_derived_ptrs() {
return{Derived_1_ptr(), Derived_2_ptr()}; // no breakpoint
}
void test_1() {
all_derived_ptrs(); // breakpoint 2
all_derived_ptrs(); // breakpoint 3
}
void test_2() {
{
std::vector<myns::shared_ptr<Base>> t{Derived_1_ptr(), Derived_2_ptr()};
}
{
std::vector<myns::shared_ptr<Base>> t{Derived_1_ptr(), Derived_2_ptr()};
}
}
int main() {
test_1();
return 0;
}
Derived_1_ptr() and Derived_2_ptr() are in fact global variables that solve the initialization order problem.
With these code:
no breakpoint
. t
is constructed, and look at t
in debug window. t[0]
has 1 strong ref and t[1]
has 2 strong refs.So I can infer that when the first vector is constructed, Derived_2_ptr()
is correctly copied (++reference_count), while Derived_1_ptr()
seems to be stealed (moved?) from the static const r (reference_count is still 1). When the first vector is destroyed Derived_2_ptr()
correctly performs --reference_count and is still alive, but Derived_1_ptr()
loses its only reference and dies before the second vector is created (it is a global static variable!).
Why? Can move constructor be applied to static const
things?
I try changing the return type of Derived_x_ptr()
to const myns::shared_ptr<Derived_x>&
or myns::shared_ptr<Base>
, and both work correctly.
Also, if I remove Derived_2_ptr() and make all_derived_ptrs return{Derived_1_ptr(), nullptr}
, Derived_1_ptr() magically works.
If I use boost::shared_ptr instead, the problem is hidden (no program stopped dialog) but ~Derived_1() is still called too early.
What is going on here? Does standard allows this kind of move? Is it a Microsoft-specific thing, or a VS2013-specific thing (I don't have another C++11 compiler)? Any ideas?
Upvotes: 1
Views: 677
Reputation: 52471
I think you might be seeing this bug: initializer_list handling broken, destructors of temporary objects called twice. Or this one (could be one and the same): Double delete in initializer_list vs 2013
Upvotes: 1