2017-01-14 13 views
3

は、それが派生クラスで、彼らが過負荷にせずに、派生型を返すような基本型を返す基底クラスの関数を定式化するためにC++で可能ですか?継承機能ではなく、基本クラス

最小例:

class Base 
{ 
    public: 
     Base(double v) 
     { 
      value = v; 
     } 

     Base add(Base b) 
     { 
      return Base(b.value + this->value); 
     } 

     void print() 
     { 
      std::cout << value << std::endl; 
     } 

     double value; 
}; 



class Derived : public Base 
{ 
    public: 
     Derived(double v) : Base(v) 
     { 

     } 

     void timesTwo() 
     { 
      value *= 2.0; 
     } 
}; 


int main() 
{ 

    Derived d1(1), d2(2); 

    // This doesn't work because the result is of type Base 
    (d1.add(d2)).timesTwo(); 


    return 0; 
} 

動機

実際の例では、Baseは線形代数行列を表し、Derivedはベクトルを表します。行列はスカラーによる加算や乗算など、ベクトルに適用可能な多くの関数を提供します。この場合

、それはベクトルを返すために、手動でこれらのすべての行列関数をオーバーライドする必要がないことが望ましいであろう。可能であれば、thisタイプが何であれ、戻りタイプはそれと同じでなければならないことを表現したいと思います。

例:

class Matrix 
{ 
    ... 
    Matrix operator*(double x); 
}; 

class Vector : Matrix 
{ 
    ... 
}; 

Matrix M; 
M = M * 2.0; // works 

Vector v; 
v = v * 2.0; // does not work, because v * 2.0 returns a Matrix 

例えばオーバーライドするための努力すべての派生クラスのoperator*()Vector2、Iは、溶液はMatrixからVectorに(及びVector3にキャストを定義することであることを理解するなど、3-ための派生クラスと2次元ベクトルが存在するという事実によって

を増加させます、...)しかし、これはすべてのエントリー(効率のために、スタック配列です)をコピーすることを伴います。

もっと効率的なソリューションはありますか?そして、いない場合、それは一般的に

  1. に、より良い/クリーナーと見なされる各派生クラスで、またはキャストを定義
  2. に関連するすべてのコードを複製?私の現在の理解で

は、競合の問題は、次のとおりです。

  1. 重複したコードは、ソリューションエラーが発生しやすいとリファクタリングがより困難になります。
  2. 既存のコードを再利用するには、「スコープ」がMatrix、Vector、Vector3、...の間で変更されるたびに多くのコピー操作が必要です。大きな計算で使用すると効率が悪くなります。

任意の提案が最もいただければ幸いです。ありがとう!

+1

[CRTP](https://en.m.wikipedia.org/wiki/Curiously_recurring_template_pattern)を見てください:また、あなたが派生クラスで必要なメソッドが純粋仮想宣言する。 – Holt

+0

私はそれがアンチパターンになると思います。 – szpanczyk

+0

代わりに操作を仮想にすることができるので、多態性だけを使用できます。しかし、それは最初にJavaやC#を使うことを示唆しています。 – Incomputable

答えて

3

はい、ただし、フリー関数(ほとんどの演算子を含む)でのみ使用できます。

template<class X, class Y, 
    std::enable_if_t<std::is_base_of<Base, std::decay_t<X>>{},int> =0, 
    std::enable_if_t<std::is_base_of<Base, std::decay_t<Y>>{},int> =0 
> 
friend X& operator+=(X&x, Y&& rhs) 
{ 
    x.value += rhs.value; 
    return x. 
} 
template<class X, class Y, 
    std::enable_if_t<std::is_base_of<Base, std::decay_t<X>>{},int> =0, 
    std::enable_if_t<std::is_base_of<Base, std::decay_t<Y>>{},int> =0 
> 
friend std::decay_t<X> operator+(X&&x, Y&& rhs) { 
    auto r=std::forward<X>(x); 
    r+=std::forward<Y>(rhs); 
    return r; 
} 

今、私はその権利、

(d1+d2).timesTwo(); 

作品をした場合。

それは通常よく働くので、私はまた、+=の面で+を実施しました。

非常に汎用テンプレートの事業者とケーニッヒ・ルックアップは、奇妙なことは、あなたがテンプレートの種類にBaseBaseから派生型を渡す場合に発生し、結果のタイプに+を使うに進めるために存在している場合空想を有効にしてください。 「Baseから派生したものだけ」と言うと、正しいことが起こります。

テンプレートのフリーフレンズ関数を使用する必要があります。そのため、戻り値の型を変更するために、テンプレート内で "*this"の型を取得できます。これは、テンプレートメンバー関数では実行できません。

enable_if句はMSVCではうまく機能しませんが、他のコンパイラではベストプラクティスです。 MSVCではenable_if=0の代わりにclass=enable_ifを使用してください。 =0が最善の理由はここでは範囲外です。

+0

すばやく返信いただきありがとうございます、私はそれがまさに私が必要としていたと信じています! – user7418923

0

クラスを作成します。基本抽象クラスとそのメソッドを分離した関数に入れます。

#include <iostream> 

class Base 
{ 
public: 
    Base(double v) : value(v) {} 

    double value; 
    virtual void timesTwo() = 0; 
}; 

class Derived : public Base 
{ 
public: 
    Derived(double v) : Base(v) {} 

    void timesTwo() 
    { 
     value *= 2.0; 
     std::cout << "timesTwo " << value << std::endl; 
    } 
}; 

template <class T> 
T add(const T& t1, const T& t2) 
{ 
    return T(t1.value + t2.value); 
} 

int main() 
{ 
    Derived d1(1), d2(2); 
    add(d1, d2).timesTwo(); 

    return 0; 
} 
関連する問題