Update
It looks like the fix for the problem has been checked in .
Interest Ask. This seems to be a bug in the way GCC handles default initialized arguments = {} , which was a late addition to the standard . The problem can be reproduced using a fairly simple class instead of std::unordered_map<int,int> :
#include <utility> struct PtrClass { int *p = nullptr; PtrClass() { p = new int; } PtrClass(PtrClass&& rhs) : p(rhs.p) { rhs.p = nullptr; } ~PtrClass() { delete p; } }; void DefArgFunc(PtrClass x = {}) { PtrClass x2{std::move(x)}; } int main() { DefArgFunc(); return 0; }
Compiled with g ++ (Ubuntu 4.8.1-2ubuntu1 ~ 12.04) 4.8.1 , it displays the same problem:
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000001aa9010 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fc2cd196b96] ./a.out[0x400721] ./a.out[0x4006ac] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fc2cd13976d] ./a.out[0x400559] ======= Memory map: ======== bash: line 7: 2916 Aborted (core dumped) ./a.out
Digging a little deeper, GCC seems to create an additional object (although it only calls the constructor and destructor once) when you use this syntax:
#include <utility> #include <iostream> struct SimpleClass { SimpleClass() { std::cout << "In constructor: " << this << std::endl; } ~SimpleClass() { std::cout << "In destructor: " << this << std::endl; } }; void DefArgFunc(SimpleClass x = {}) { std::cout << "In DefArgFunc: " << &x << std::endl; } int main() { DefArgFunc(); return 0; }
Output :
In constructor: 0x7fffbf873ebf In DefArgFunc: 0x7fffbf873ea0 In destructor: 0x7fffbf873ebf
Changing the default argument from SimpleClass x = {} to SimpleClass x = SimpleClass{} causes
In constructor: 0x7fffdde483bf In DefArgFunc: 0x7fffdde483bf In destructor: 0x7fffdde483bf
as was expected.
It seems that the object is being created, the default constructor is called, and then something similar to memcpy is executed. This "ghost object" is what is passed to the move constructor and modified. However, the destructor is called on the original, unmodified object, which now shares some pointer with the object created by the motion. Both eventually try to free him, causing a problem.
The four changes you noticed corrected the problem, given the above explanation:
Passing an argument to the constructor ( Bar b(Foo{}); ) instead of using the default argument also solves the problem.