Double delete in initializer_list vs 2013
The initializer_list behavior is buggy. In its destructor it calls a vector delete (a delete[]) of the entire range and then deletes the first entry in the array again.
This behavior is not part of the initializer_list class and looks like a compiler bug. initializer_list doesn't have a destructor and doesn't allocate the array used for the list. It just looks like a wrapper for a C array.
As for using the extra copy you see, it's caused by the vector resizing from during its initialization.
Here's your flow:
Init 00B7FAE8 // construct "foo"
Init 00B7FBE8 // construct "bar"
Copy 00E108A0 // copy "foo" to vector (capacity=1)
Copy 00E10AE8 (?????) // copy the above object to the resized vector (capacity = 2)
Deleting b 00E108A0 // delete the smaller vector buffer
Copy 00E10BE8 // copy "bar" from initialization_list to vector
Deleting b 00B7FBE8 // delete initialization_list in reverse order. this is "bar"
Deleting b 00B7FAE8 // last to delete. this is "foo"
Deleting b 00B7FAE8 (bug)
// later C::bs is destroyed
What you can see here is the initializing a vector via push_back is quite slow due to copying. This would be happen even if you've used the more elegant way:
C(initializer_list<B> bb) : bs(bb) {}
A faster (no extra copies) method is:
C(initializer_list<B> bb) {
bs.reserve(bb.size());
bs.insert(bs.end(), bb.begin(), bb.end());
}
Why first element is destroyed?
What happens is that a bug in VS2013 causes a double delete on the first item of the initializer_list.
Here's the flow:
- initializer_list is constructed.
- target vector is reserved a size of 1 and first item is copied (via copy constructor).
- vector grows slowly to initializer_list size.
- initializer_list is destroyed via a vector-destructor (i.e.
delete[]
). Last element is destroyed first. - First element is destroyed again via scalar-destructor (i.e.
delete
).
I've seen this on another post and verified the behavior using a debugger.
See here
For VS2013, initializer_list is good for basic types only.
Crash in VS2013 SP5 when inserting initializer_list into vectorstring
You would be surprised if you try inserting 3 elements like x.insert(x.end(), {"e","f", "g"});
. No crash, right!
This is a bug in MSVC 2013 and seems to be fixed in MSVC 2015. Here is an explanation to shed some light to where the problem occurs. If you try only declaring std::string a{ "a", "b" };
you will see that it compiles fine but it causes a crash. If you add one more element, then it won't compile at all. So, what happens with the insert is that it calls the string
constructor instead of calling vector
constructor.
VC++ vector as default parameter
VC2013 internal bug. It's not entirely up to date with new C++ constructs.
The problem surfaces in the debug delete code, in particular code that's checking against double deletes and deletion of non-heap-allocated objects. Since the relevant objects are std::vector
and std::string
, there are roughly four options:
- Bug in
std::string
implementation - Bug in
std::vector
implementation - Bug in dbgdel.cpp code which checks
delete
in debug mode - Compiler bug in default argument code
Considering the two test cases, the first three are rather unlikely.
Edit2: From the duplicate: "The initializer_list
behavior is buggy. In its destructor it calls a vector delete (a delete[]
) of the entire range and then deletes the first entry in the array again)." initializer_list
is part of the implementation, so this is indeed an internal error in VS2013.
std::unique_ptr deleted function, initializer_list - driven allocation
According to Paragraph 8.5.1/2 of the C++11 Standard:
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer listare taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. [...]
For each element, then, copy-initialization involves the creation of a temporary of the destination type, which is then use to copy-construct the element of the array.
However, your class contains a member whose type is an instance of unique_ptr
, which is non-copyable. That makes your class non-copyable as well.
Moreover, although unique_ptr
is moveable, your class is not, because the implicit generation of a move constructor by the compiler is suppressed by the presence of an explicitly defined destructor. If that were not the case (i.e., if you explicitly defined a move constructor for your class), copy-initialization would work (see 8.5/15).
Try changing the definition of WFactory
as follows to see that:
class WFactory
{
public:
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
WFactory(WFactory&& f) : _w(std::move(f._w)) {}
~WFactory() { _w.reset(nullptr); }
private:
std::unique_ptr<Widget> _w;
};
int main()
{
std::unique_ptr<Widget> a(new W1());
WFactory wf[] { 4, "msg" }; // OK
}
Abstract class as std::initializer_list object
Somewhat hackish approach using a initializer_list<base *>
:
template<class... Ts>
void foo_everything(Ts&&... args){
std::initializer_list<base *> list = {&args...};
for(auto i : list) i->foo();
}
Then remove the braces from the call:
foo_everything(derived(1), derived(2), derived(3));
If you don't really need to convert to base *
and perform a virtual call, and just want to call foo()
on each object passed in, then we can use the usual pack-expansion-inside-an-initializer-list trick:
template<class... Ts>
void foo_everything(Ts&&... args){
using expander = int[];
(void) expander { 0, ((void) std::forward<Ts>(args).foo(), 0)...};
}
Related Topics
Integer Division Rounding with Negatives in C++
What's the Right Way to Use the Rand() Function in C++
Differencebetween Exit() and Abort()
Differencebetween a Static and Const Variable
Get Absolute Value Without Using Abs Function Nor If Statement
When Can You Omit the File Extension in an #Include Directive
Are There Any MACros to Determine If My Code Is Being Compiled to Windows
How to Draw Text with Glut/Opengl in C++
Why Is Clocks_Per_Sec Not the Actual Number of Clocks Per Second
Is "Inline" Implicit in C++ Member Functions Defined in Class Definition
Order of Constructor Call in Virtual Inheritance
Allocating a Large Memory Block in C++
How to See the Template Instantiated Code by C++ Compiler
Checking for Eof in String::Getline