2017年10月9日

[C++] covariance / contravariance

covariance / contravariance 是跟繼承有密切關聯的產物,而且與語言規範有關。然而這邊雖然要講這兩項性質,實際上 C++ 的語言規範上是 invariance,因此若要達到類似效果,在 C++ 必須要在類別設計時就加以考量。

 covariance / contravariance 簡單來說,就是在特性情況下,子型別與父型別之間能否接受互相轉換。這個特定情況舉例來說,就是放在 container 內的狀況。

以下舉例說明:

class Base {};
class Derive {};

void print(std::vector<Base*>&);

int main()
{
    std::vector<Base*> b;
    std::vector<Derive*> d;
    print(b);    // pass
    print(d);    // ok if C++ covariance

    std::shared_ptr<Base> B;
    std::shared_ptr<Derive> D;
    B = D; // ok in C++
}

在這個例子中因為 C++ 是 invariance,所以 print(d) 這一行編譯會失敗,其原因在於
std::vector<Base*> 與 std::vector<Derive*> 要視為不同型別
即便 Derive 是繼承自 Base 也一樣。然而,若 C++ 支援 covariance 的話,這種轉換就是允許的,因為 Derive class pointer 本來就可以被丟向 Base class pointer。

由於 C++ 本身是 invariance,沒有 covariance 的特性,因此在 smart pointer (像是上面例子中的 shared_ptr) 就會需要謹慎架構。上面例子中若 shared_ptr 沒有考量到這個問題就會導致 smart pointer 在使用上喪失了 raw pointer 的部分特性。而要處理這個問題說穿了很簡單,架構類別時加上適當的型態轉換機制,讓型別轉換可以運作在設計的類別上即可。不過如果既有的類別沒有提供型別轉換的機制去模擬 covariance/contravariance 的效果就一定辦不到就是了,畢竟 C++ 本身是 invariance。

至於 covariance 跟  contravariance 的差異簡單來說,就是 covariance 是 Derive 轉成 Base,而 cotravariance 是要讓 Base 可以轉成 Derive (所以 Derive class 必須提供參數型態是 Base 的 copy ctor)。至於 C++ 是 invariance 的原因查到的說法是 covariance/contravariance 會讓型別檢查變得複雜甚至無效,原因的話在 stackoverflow 上的 C++ Templates polymorphism 這一篇有提供例子做說明了。

沒有留言:

張貼留言