2021年5月14日

[C++] Error Out Calling Overloaded Function with Type Conversion

故事的情境是這樣的:

某支 function 的設計,假設叫 func 好了,他只有一個參數 param,但是 param 有多種可能的型別,比方說 int, bool, ... 等等,根據不同的型別實作上會稍有不同,為此根據 param 的不同型別提供不同的 overloading 實作,讓使用者可以無需顧慮型別問題。

問題是,當今天使用者傳進 param 的型別,假設是 char,根據預期應該要去呼叫對應到 char 的實作,但是目前的實作中並沒有提供,理想上是應該對此另行提供一個符合 char 的實作,然後根據 C++ overloading resolution 的機制,此時會去呼叫 param 型別是 int 的實作從而導致可能有非預期的結果。

理想上此時應該在開發階段時就以某種方式告知開發者此處有缺漏須補足,但,該怎麼做?

  好的,首先直接以 code 來看,預期的結果應該要是怎樣:
void func(int param);
void func(bool param);

int main()
{
    func(1);     // call func(int)
    func(false); // call func(bool)
    func('x');   // error out!
    return 0;
}

基於 compiler 本身是沒有特定的 option 可以告知有這種情況發生,因此解法是利用 overloading resolution 的先後順序製造一個陷阱,當有這類情況發生時可以在不論是編譯期或執行期跑出錯誤提醒開發者:

void func(int param);
void func(bool param);

template <typename T, bool b=false>
void func(T param)
{
    static_assert(b); // trap!
}

int main()
{
    func(1);     // call func(int)
    func(false); // call func(bool)
    func('x');
    return 0;
}

概念其實很簡單:當有完全吻合的型別時,compiler 優先選擇完全吻合的實作,然而每個現有的實作的型別都無法完全吻合時,這時 compiler 會從 template 特化出指定的型別,所以只要在這個地方埋個陷阱即可。而這個陷阱可以是 compile time 有作用的 (以上面的例子就是 static_assert),或者是 runtime 有作用的 (比方說 assert)。如此一來當 template 一被特化出特定型別就會觸動陷阱,從而導致編譯失敗或執行後有錯誤來提醒開發者該補上符合該型別的實作。

這邊需要注意的是之所以不寫成 static_assert(false); 的原因是我們希望的效果是只有在 template 特化出特定型別的時候才會觸動這個陷阱,而 static_assert(false); 以及其他語法錯誤的寫法會讓 compiler 在即便沒有特化 template 時也告知有錯誤,因此無法達成預期的效果。

2 則留言: