2013-04-03 67 views
14

看一看這段代碼:奇怪鏘行爲

#include <iostream> 
#include <string> 

void foo(int(*f)()) { 
    std::cout << f() << std::endl; 
} 

void foo(std::string(*f)()) { 
    std::string s = f(); 
    std::cout << s << std::endl; 
} 

int main() { 
    auto bar = []() -> std::string { 
     return std::string("bla"); 
    }; 

    foo(bar); 

    return 0; 
} 

編譯它

g++ -o test test.cpp -std=c++11 

導致:

bla 

像它應該做的。與

clang++ -o test test.cpp -std=c++11 -stdlib=libc++ 

編譯它會導致:

zsh: illegal hardware instruction ./test 

而且隨着

clang++ -o test test.cpp -std=c++11 -stdlib=stdlibc++ 

編譯它也導致:

zsh: illegal hardware instruction ./test 

鏘/ GCC版本:

clang version 3.2 (tags/RELEASE_32/final) 
Target: x86_64-pc-linux-gnu 
Thread model: posix 

gcc version 4.7.2 (Gentoo 4.7.2-r1 p1.5, pie-0.5.5) 

任何有什麼建議是什麼問題?

在此先感謝!

+9

我會說這是叮鐺中的一個錯誤 – 2013-04-03 21:30:39

+0

僅供參考,有關[ud2和clang]的更多信息(http://stackoverflow.com/q/26309300/1708801) – 2015-03-19 14:47:25

回答

5

這很可能是叮噹3.2中的一個錯誤。我無法用鏗鏘樹幹重現崩潰。

+0

肯定的。樹幹似乎工作正常。所以它可能應該在3.3版中修復。 – rralf 2013-04-04 10:01:22

11

是的,這是Clang ++中的一個錯誤。我可以在i386-pc-linux-gnu中用CLang 3.2重現它。

現在一些隨機分析...

我發現的bug是從labmda轉換爲指針到函數:編譯器創建一種與適當的簽名調用lambda,但它的指令ud2而不是ret

大家都知道,指令ud2是一條明確提出「無效操作碼」異常的指令。也就是說,一條指令故意不定義。

看看拆機:這是thunk函數:

main::$_0::__invoke(): 
     pushl %ebp 
     movl %esp, %ebp 
     subl $8, %esp 
     movl 8(%ebp), %eax 
     movl %eax, (%esp) 
     movl %ecx, 4(%esp) 
     calll main::$_0::operator()() const ; this calls to the real lambda 
     subl $4, %esp 
     ud2 ; <<<-- What the...!!! 

因此,錯誤的小例子將是簡單的:

int main() { 
    std::string(*f)() = []() -> std::string { 
     return "bla"; 
    }; 
    f(); 
    return 0; 
} 

奇怪的是,該錯誤不會如果返回類型是簡單類型,例如int,則會發生。然後將生成的是:

main::$_0::__invoke(): 
     pushl %ebp 
     movl %esp, %ebp 
     subl $8, %esp 
     movl %eax, (%esp) 
     calll main::$_0::operator()() const 
     addl $8, %esp 
     popl %ebp 
     ret 

我懷疑,這個問題是在返回值的轉發。如果它適合註冊表,如eax一切順利。但是,如果它是一個大結構,例如std::string它會在堆棧中返回,編譯器會感到困惑,並在絕望中發出ud2