VC2013的一個bug


前段時間在嘗試使用一個C++的GUI庫nana。這個庫最大的特點在於使用現代C++風格去編寫GUI程序,而不需要使用大量的比較丑陋的代碼(如MFC中的各種宏),或者其它的非C++元素。這是一個比較新的庫,作者是個中國人,有興趣的朋友可以去試一試,由於使用大量的C++11特性,所以需要VC2013或者GCC4.7以上的編譯器。使用過程中無意間發現了VC2013的一個重載決議(overload resolution)上的一個bug,這邊貼出來跟大家分享一下,或許可以幫助大家少走點彎路。

我寫了以下簡單的代碼來重現這個問題:

#include <iostream>
#include <string>

class foo{
public:
    void bar(std::string = {});
};

void foo::bar(std::string str)
{
    std::cout << str << std::endl;
}

int main()
{
    foo  f;
    f.bar();
}

編譯運行這段代碼,彈出一個錯誤:

image

按照錯誤提示,可以找到對應的代碼1168行:

image

也就是這一行:

_DEBUG_POINTER(_Ptr);

加個斷點可以發現這里面_Ptr為空指針,所以導致程序crash。這個問題其實比較容易理解,寫一個更簡單的例子就可以發現問題:

#include <string>

int main()
{
    std::string str = nullptr;
}

這段代碼通過nullptr去初始化一個string,而這顯然是錯誤的。

但問題在於,在最初的那段代碼中,

void bar(std::string = {});

這個函數聲明明明是給了函數bar一個默認參數,使其構造出一個空字符串。但為什么會調用xstring中的1168行呢?也就是參數為一個指針的assign函數呢?

經過多次試驗以后發現,VC將上面這個聲明理解為了:

void bar(std::string = {nullptr});

也就是用一個空指針去初始化一個string,從而導致了問題。同樣的代碼,在GCC4.8中是沒有任何問題的。並且如果把

void bar(std::string = {});

改為:

void bar(std::string = std::string());

在VC2013里也沒有問題了。這也就說明了VC2013在處理{}來初始化對象時的確存在重載決議的問題。

之所以之前說這個問題比較隱蔽,是因為這種情況只出現在類的成員函數中,如果是普通函數,就沒有這個問題:

#include <iostream>
#include <string>

void func(std::string = {});

int main()
{
    func();
}

void func(std::string str)
{
    std::cout << str << std::endl;
}

上面這段代碼和剛開始那段代碼幾乎一樣,唯一的不同在於func是一個普通函數,而bar是一個成員函數。

不知道大家有沒有遇到類似的問題?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM