48240


共変的についてなのですが参考書のほうに説明があるのですがさっぱりわかりませんので  
わかりやすく説明していただけますでしょうか？(新版明解c++中級編)

関数`derived::f`が関数`base::f`をオーバーライドしているとき、以下に示す全条件が成立すること。

  1. 両者の返却値型が、クラスへのポインタか参照であること。
  2. `base::f`の返却値型のクラスが`derived::f`の返却値の型のクラスと同じであるかまたは、それが`derived::f`の返却値の型のクラスの、あいまい性がなくてアクセス可能な、直接もくは間接の基底クラスとなっている、
  3. ポインタまたは参照が同一のcv修飾を持ち、`derived::f`の返却値のクラス型のcv修飾が`base::f`の返却値のクラス型のcv修飾より多くない(cv修飾はconstとvolatile)

[code]

    class t {
    public:
        virtual t* f() const = 0;
        t(){ }
    };
    
    class tA : public t {
    private:
    
    public:
        virtual tA* f()const { cout << "tA\n"; return new tA; }
        tA(){ }
    };
    
    class tB : public t {
    public:
        virtual t* f()const {
            cout << "tb\n";
            return new tB;
        }
        tB() { }
    };
    
[/code]


CC BY-SA 4.0
2018-09-07T14:05:08.007
48192
2018-09-10T08:25:49.597
2018-09-07T14:19:45.960
19110
question
c++
c++のクラスの共変的の意味がわからないです。


[c++ 継承クラスを理解したい](https://ja.stackoverflow.com/questions/44708/)  
で質疑応答した通り、

  * 基底クラスとはより抽象的なもの、派生クラスとはより具象的なもの
  * 基底クラスへのポインタ・参照で、派生クラスのインスタンスを指すことができる
  * 派生させる側はその必要があったから派生させたのであって、派生クラスを書いたプログラマにとっては、基底クラスへのポインタ・参照があってもほぼ無意味（派生クラスインスタンスへのポインタ・参照が必要）

ってあたりが「身についている」前提で話します。

共変の解説としては、例えば Wikipedia  
<https://ja.wikipedia.org/wiki/%E5%85%B1%E5%A4%89%E6%80%A7%E3%81%A8%E5%8F%8D%E5%A4%89%E6%80%A7_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%A6)>  
「広い型」→「狭い型」へ変換できることとあります。ただし、ここでの意味合いは **概念的に** 広い、狭い、であることには注意が必要です。概念が広い＝より
**抽象的＝基底クラス** 、概念が狭い＝より **具象的＝派生クラス** 、であって、メモリの消費量が狭い広いではありません。

* * *

提示コード例 `tB::f() const`
は、共変を使わずに実装されています。返却値は暗黙のアップキャスト（基底クラスへのポインタで派生クラスオブジェクトが正しく指し示せる）で変換され正しく動きます。その意味では問題はありません。

しかし `virtual` なメンバ関数は基底クラス `t` からしか呼べない・呼んではいけないわけではなくて、派生クラス（っつか自分自身）である `tB`
から呼び出してもよいわけです。するとちょっと困ったことになります。

[code]

    // tB のメンバ関数 func() を追加したとします
    void tB::func() {
        tB* p=this->f(); // コンパイルエラー（ダウンキャスト）
    }
    
[/code]

派生クラス作ったほうは派生クラス（へのポインタや参照）を使いたいのであって、基底クラスに縛られたら「自分が実装した機能が使えない」わけで、なにもうれしくありません。

共変を使ってより **狭い概念＝派生クラス** を返すようにした `tA` のほうは

[code]

    // tA のメンバ関数 func() を追加したとします
    void tA::func() {
        tA* p=this->f(); // エラーにならない
    }
    
[/code]

より一層、自分（＝派生クラス実装者）のやりたいことが簡単にできるようになりました。もちろん、基底クラス `t` から `tA::f()`
を呼び出しても問題ありません。

以上で共変とは何かの解説とします。で、質問に挙げられている細かい条件については省略します。（共変と名乗ってよい条件の解説にすぎず、事の本筋ではないので）


CC BY-SA 4.0
2018-09-10T00:18:25.410
48240
2018-09-10T00:18:25.410
8589
48192
answer


C++言語での該当仕様を理解するには、前提条件としてクラス間の派生(derived)関係と、メンバ関数オーバーライド(override)の基本ルールを把握しておく必要があります。

C++においては「関数オーバーライド時に戻り値型・各引数の型を変更してはいけない」という基本ルールがあります。

[code]

    class Base {
      virtual int mf() { /*...*/ }
    };
    
    class Derived : public Base {
      // NG: コンパイルエラー！
      //   派生クラスでの関数mfオーバーライド時に戻り値型を
      //   int → double へと変更しようとしている。
      virtual double mf() { /*...*/ }
    };
    
[/code]

このルールの **特例措置** として、該当参考書が説明する 共変( _covariant_ ) という概念が登場します。

[code]

    class Base {
      virtual Base* clone() { /*...*/ }
    };
    
    class Derived : public Base {
      // OK:
      //   関数cloneオーバーライド時に戻り値型を
      //   Base* → Derived* へと変更している。
      //   この戻り値型変更だけは特別に許可されている。
      virtual Derived* clone() { /*...*/ }
    };
    
[/code]

この特別ルールのユースケースとして、上記例示コードのようなオブジェクト複製(clone)操作がよく取り上げられます。つまり基底クラス`Base`の複製操作では`Base`型を返し、派生クラス`Derived`の複製操作では`Derived`型を返したいという使い方です。


CC BY-SA 4.0
2018-09-10T07:04:27.757
48251
2018-09-10T08:25:49.597
2018-09-10T08:25:49.597
49
49
48192
answer