2014-01-07 23 views
48

就像標題所暗示的,我有一個簡短的演示程序,可以編譯所有這些編譯器,但在使用gcc 4.8和gcc 4.9編譯後運行核心轉儲:爲什麼Clang和VS2013接受移動大括號初始化的默認參數,但不接受GCC 4.8或4.9?

有關爲什麼?

#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); 
} 

我的編譯設置:

g++ --std=c++11 main.cpp 

下面是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 ***

+2

你在gcc上使用了哪些標誌? – ThomasMcLeod

+0

爲什麼在使用這個構造函數時會發生:'Bar(Foo f = {Foo()}):_f(std :: move(f)){} – ThomasMcLeod

+0

它的核心轉儲,原因相同。 – vmrob

回答

11

更新

它會出現一個fix for the problem has been checked in


有趣的問題。它肯定似乎是GCC處理= {}初始化默認參數的一個錯誤,這是一個late addition to the standard。這個問題可以用一個非常簡單的類的地方被複制的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,它顯示了同樣的問題:

*** 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 

挖得更深一些,GCC似乎產生了一個額外的對象(雖然它只是調用構造函數和析構函數各一次),當您使用此語法:

#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 

改變從SimpleClass x = {}默認參數SimpleClass x = SimpleClass{}產生

In constructor: 0x7fffdde483bf 
In DefArgFunc: 0x7fffdde483bf 
In destructor: 0x7fffdde483bf 

如預期。

似乎在發生的事情是創建對象,調用默認構造函數,然後執行與memcpy類似的操作。這個「鬼物體」是傳遞給移動構造函數並進行修改的東西。但是,析構函數是在原始的,未修改的對象上調用的,該對象現在與移動構造的對象共享一些指針。兩者最終都試圖釋放它,造成這個問題。

,你注意到的四個轉變固定給出的上述解釋的問題是有意義的:

// 1 
// adding the destructor inhibits the compiler generated move constructor for Foo, 
// so the copy constructor is called instead and the moved-to object gets a new 
// pointer that it doesn't share with the "ghost object", hence no double-free 
~Foo() = default; 

// 2 
// No `= {}` default argument, GCC bug isn't triggered, no "ghost object" 
Bar(Foo f = Foo()) : _f(std::move(f)) {} 

// 3 
// The copy constructor is called instead of the move constructor 
Bar(Foo f = {}) : _f(f) {} 

// 4 
// No `= {}` default argument, GCC bug isn't triggered, no "ghost object" 
Foo f1 = {}; 
Foo f2 = std::move(f1); 

將參數傳遞給構造(Bar b(Foo{});),而不是使用默認的說法也解決了這個問題。

+0

是的,這絕對是一個在gcc 4.9(或至少我擁有的快照)中存在的bug。clang沒有相同的錯誤。已經有一個錯誤報告和其他一些看似相似的報告。但是爲了澄清測試用例的問題+1。如果您執行'SimpleClass x = SimpleClass {}',問題就會消失。 – 2014-01-15 19:39:30

+0

已經有報道! http://gcc.gnu.org/bugzilla/show_bug。cgi?id = 59713 – vmrob

+0

是的,對不起,我現在看到關於這個問題的完整評論鏈。我以前沒有讀過所有隱藏的評論。哦,我發現它是一個有趣的練習:) – jerry

相關問題