Like the title suggests, I have a short demo program that compiles on with all of those compilers, but core dumps when ran after compiling with gcc 4.8 and gcc 4.9:
Any ideas as to why?
#include <unordered_map>
struct Foo : std::unordered_map<int,int> {
using std::unordered_map<int, int>::unordered_map;
// ~Foo() = default; // adding this allows it to work
};
struct Bar {
Bar(Foo f = {}) : _f(std::move(f)) {}
// using any of the following constructors fixes the problem:
// Bar(Foo f = Foo()) : _f(std::move(f)) {}
// Bar(Foo f = {}) : _f(f) {}
Foo _f;
};
int main() {
Bar b;
// the following code works as expected
// Foo f1 = {};
// Foo f2 = std::move(f1);
}
My compilation settings:
g++ --std=c++11 main.cpp
Here is a backtrace from GDB:
#0 0x00007fff95d50866 in __pthread_kill ()
#1 0x00007fff90ba435c in pthread_kill ()
#2 0x00007fff8e7d1bba in abort ()
#3 0x00007fff9682e093 in free ()
#4 0x0000000100002108 in __gnu_cxx::new_allocator<std::__detail::_Hash_node_base*>::deallocate ()
#5 0x0000000100001e7d in std::allocator_traits<std::allocator<std::__detail::_Hash_node_base*> >::deallocate ()
#6 0x0000000100001adc in std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<int const, int>, false> > >::_M_deallocate_buckets ()
#7 0x000000010000182e in std::_Hashtable<int, std::pair<int const, int>, std::allocator<std::pair<int const, int> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_deallocate_buckets ()
#8 0x000000010000155a in std::_Hashtable<int, std::pair<int const, int>, std::allocator<std::pair<int const, int> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::~_Hashtable ()
#9 0x000000010000135c in std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<int const, int> > >::~unordered_map ()
#10 0x00000001000013de in Foo::~Foo ()
#11 0x0000000100001482 in Bar::~Bar ()
#12 0x0000000100001294 in main ()
*** error for object 0x1003038a0: pointer being freed was not allocated
***
Update
It appears a fix for the problem has been checked in.
Interesting question. It definitely seems to be a bug with how GCC handles
= {}
initialized default arguments, which was a late addition to the standard. The problem can be reproduced with a pretty simple class in place ofstd::unordered_map<int,int>
:Compiled with g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1, it displays the same problem:
Digging a little deeper, GCC seems to create an extra object (though it only calls the constructor and destructor once each) when you use this syntax:
Output:
Changing the default argument from
SimpleClass x = {}
toSimpleClass x = SimpleClass{}
producesas expected.
What seems to be happening is that an object is created, the default constructor is called, and then something similar to a
memcpy
is performed. 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 move-constructed object. Both eventually try to free it, causing the issue.The four changes that you noticed fixed the problem make sense given the above explanation:
Passing an argument to the constructor (
Bar b(Foo{});
) rather than using the default argument also solves the problem.