假設有一個 class 如下:
class Foo {
public:
Foo(int i) : i_(i) {}
private:
int i_;
};
constructor 有一個參數,我們正常的宣告它
Foo f(0);
一切都很好,直到我們有了一個函式,它接收一個 Foo 參數
void Func(Foo f)
{}
我們預期此函式會被這樣呼叫
Func(f);
但有人不小心寫成
Func(0);
你會很驚訝地發現竟然可以編譯成功,因為編譯器發現 Func 需要參數 Foo,而 0,這個 int 型別可以被傳進 Foo 的建構子以建構出 Foo,而編譯器就這麼幹了,這非常可能不是你所預期的行為。
但只要為建構子加上 explicit,告訴編譯器只能顯示建構,編譯器就不會隱式轉換讓上述函式呼叫通過編譯。
class Foo {
public:
explicit Foo(int i) : i_(i) {}
…
};
那麼建構子有哪些情況要加上 explicit 呢?
1. 建構子只有一個參數時。有兩個以上的參數不用加,因為不會只傳一個參數進函式就可以建構出 Foo。
class Foo {
public:
Foo(int i, double d) : i_(i), d_(d) {} // 不用加 explicit,因為無法這樣呼叫 Func(0, 1.0)
…
};
2. 有多個參數,但是第二個參數有參數預設值。這很好理解,因為只傳一個值,Func(0) 還是可以通過編譯。
class Foo {
public:
explicit Foo(int i, double d = 1.0) : i_(i), d_(d) {} // 要加 explicit
…
};
上述情況其實就隱含著如果只有一個參數,但是有參數預設值,或是有多個參數,但第一個參數就有參數預設值的情況,因為 C++ 是前面的參數有預設值,後面就都要有,這應該很好理解。
class Foo {
public:
explicit Foo(int i = 0) : i_(i), d_(1.0) {} // 只有一個參數,且有預設值,要加
explicit Foo(int i = 0, double d = 1.0) : i_(i), d_(d) {} // 有多參數,但第一個參數就有預設值,也要加
…
};
曾經我就遇過這樣一個 bug,vc6 的 CString 建構子因為沒有宣告 explicit,有人想建構一個 “0” 字串,結果傳進的是整數 0,導致後來程式整個亂掉,為了你的肝,請養成好習慣為建構子加上 explicit。
對了,如果你記不起來規則,那只要是建構子不管幾個參數你都加 explicit 算了。
等等!
你以為這樣就結束了?
還有一種函式要加 explicit,那就是轉型運算子
class Foo {
public:
explicit operator bool() {
return true;
}
…
};
假設有一個 Func2,接收一個 bool 參數
void Func2(bool b)
{}
如果你不加,那這兩種用法都會通過編譯,但應該不是你要的行為
Func2(f);
if (f)
{
…
}
但如果加了,只有
if (f)
{
…
}
會通過編譯,而現在 Func2 呼叫需要明確轉型才行
Func2(static_cast<bool>(f));
或
Func2((bool)f);
這次終於是全部了。
留言
張貼留言