我可以看到目前的兩個問題:
1:最傷腦筋的解析
正如在評論Brian指出,這條線實際上是一個函數原型:
ChildException exc();
它可以被讀爲ChildException
,其名稱爲exc
通過調用默認構造函數來初始化,或者作爲名爲012的函數返回ChildException
;我不確定確切的原因,但C++標準規定,在這種情況下,它將被視爲後者。
有三種方式來解決這個問題:
刪除括號:如果你只是調用默認的構造函數,你可以把它寫不帶括號。然而,這並不總是一種選擇,因爲當你嘗試使用通過函數調用獲得的值進行直接初始化時,最令人頭疼的解析也會讓你無法應付。
ChildException exc;
// Most vexing parse:
ChildException ce;
ChildException ce2(ce);
// This is safe, it can't be read as a function prototype.
ChildException ce3(ChildException());
// This will be parsed as a function with:
// Return type: ChildException
// Parameter: Function pointer of type "ChildException (*)()".
使用拷貝初始化:您可以分配語法,編譯器會通過複製省略優化掉其初始化。
ChildException exc = ChildException();
這工作,但看起來不必要笨重,並且運行的效率較低,如果你遇到無法進行復制省略編譯器的風險。
使用統一初始化:從C++ 11開始,當使用支持統一初始化*的編譯器時,可以使用大括號代替圓括號來指定構造函數調用;考慮到問題的標籤,我會推薦這種方法。
ChildException exc{};
* [出三個「最大」的編譯器,統一初始化由鏘3.1或更高版本的支持,GCC 4.6及更高版本,和Visual Studio 2013及更高版本。雖然GCC從4.4開始支持它,並且Visual Studio在2012 CTP中支持它,但在某些情況下,早期版本很難使用它;我不確定Clang的早期版本是否有問題。]
2:鑽石問題
我會假設你遇到麻煩的代碼是兩個註釋行:
//std::cout << static_cast<std::exception> (exc).what() << std::endl;
//std::cout << static_cast<std::runtime_error> (exc).what() << std::endl;
或者更具體地說,問題是這些行中的第一行導致「模糊轉換」錯誤,而第二行正常工作。這是因爲ChildException
實際上有兩個std::exception
基類,其中每個基類都與另一個分開。這個類的佈局看起來像這樣,具體是:
class ChildException size(28):
+---
| +--- (base class Exception)
0 | | {vbptr}
| +---
+---
+--- (virtual base runtime_error)
| +--- (base class exception)
4 | | {vfptr}
8 | | _Mywhat
12 | | _Mydofree
| | <alignment member> (size=3)
| +---
+---
+--- (virtual base exception)
16 | {vfptr}
20 | _Mywhat
24 | _Mydofree
| <alignment member> (size=3)
+---
注意,如果你願意,當你的Exception
幾乎從std::exception
繼承,std::runtime_error
沒有。因此,其std::exception
基地與您的Exception
的std::exception
基地截然不同,因此任何將ChildException
轉換爲std::exception
的嘗試都不明確,因爲它可能是指ChildException::Exception::exception
基地或基地的基地。如果可能的話,我會建議重構你的異常類,以便每個異常類最多繼承一個異常類。如果這是不可能的,你可以通過一個基類投它:
// Cast into std::exception through the base classes:
std::cout << "As Exception: "
<< static_cast<std::exception>(static_cast<Exception>(exc)).what()
<< std::endl;
std::cout << "As runtime_error: "
<< static_cast<std::exception>(static_cast<std::runtime_error>(exc)).what()
<< std::endl;
不建議這樣做,因爲造成鑽石問題的問題,但如果需要的話它是可用的。這是因爲其中每一個都會訪問不同的std::exception
:第一個將在班級佈局結束時訪問virtual
,第二個將訪問std::runtime_error
中的一個。
*代碼編譯但不起作用* - 你是什麼意思不起作用? –
'exc'已被聲明爲一個返回'ChildException'的函數。 – Brian
只是一個挑剔的問題,但構造函數'std :: exception(const char * const&)'是一個非標準的擴展。雖然Visual Studio使用它,但我不確定是否有其他主要的編譯器。剛剛想到我現在指出了這一點,因爲如果你將來發現自己使用了不同的編譯器,它可能會回來咬你。 –