0

基本クラスとそこから派生した 2 つのクラスがあるとします。

class Base
{
protected:
    double value;
public:
    virtual ~Base();

    Base(double value) : value(value) {}
    Base(const Base& B) { value=B.value; }

    Base operator+ (const Base& B) const { 
        return Base(value+B.value); 
    }

};

class final Derived1 : public Base {
public:
    Derived1(double value) : Base(value) {}
};

class final Derived2 : public Base {
public:
    Derived2(double value) : Base(value) {}
};

私は次のことを達成したい:

int main(int argc, char *argv[])
{
    Derived1 a = Derived1(4.0);
    Derived2 b = Derived2(3.0);

    a+a; // this should return a Derived1 object
    b+b; // this should return a Derived2 object

    a+b; // this should FAIL AT COMPILE TIME

    return 0;
}

つまり、継承されたオブジェクトが呼び出し元のインスタンスと同じ型のoperator+オブジェクトに対してのみ動作することを保証したいと考えています。

これをきれいにするにはどうすればよいですか?クラスごとに演算子を再定義していることに気付きました。

class final Derived1 : public Base {
    ...
    Derived1 operator+ (const Derived1& D1) const {
        return Derived1(value+D1.value);
    }
    ...
};

class final Derived2 : public Base {
    ...            
    Derived2 operator+ (const Derived2& D1) const {
        return Derived2(value+D1.value);
    }
    ...
};

しかし、それはただの痛みです。さらに、適切なコードの再利用のようには思えません。

ここで使用する適切なテクニックは何ですか?

4

3 に答える 3

5

Derived1およびがリーフ クラスであることを確認できる場合Derived2(つまり、他のクラスがそれらから派生できない場合)、不思議なことに繰り返しテンプレート パターンを使用してこれを行うことができます。

template <typename T>
class BaseWithAddition : public Base {
    T operator+(T const& rhs) const {
        return T(value + rhs.value);
    }
};

class final Derived1 : public BaseWithAddition<Derived1> {
    // blah blah
};

class final Derived2 : public BaseWithAddition<Derived2> {
    // blah blah
};

(finalこれ以上の派生を防ぐ C++11 の機能です。)

からの派生を許可するDerived1Derived2、問題が発生します。

class Derived3 : public Derived1 {};
Derived3 d3;
Derived1 d1;
Derived1& d3_disguised = d3;
d1 + d3_disguised; // oooops, this is allowed

コンパイル時にこれを防ぐ方法はありません。また、許可したい場合でも、複数のディスパッチなしでこの操作の適切なセマンティクスを取得するのは簡単ではありません。

于 2012-10-05T09:00:17.427 に答える
1

特殊なテンプレート関数を使用して値を追加できます。残念ながら、このトリックは演算子では機能しません。型が同じでない場合は失敗し、適切な型が返されます。

#include <type_traits>
class Base;
template <class Derived>
Derived add(const Derived& l, const Derived& r, 
            typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL);


class Base
{
 ...
    template <class Derived>
    friend Derived add(const Derived& l, const Derived& r, 
       typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL);
};

template <class Derived>
Derived add(const Derived& l, const Derived& r, 
 typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL) 
{ 
    return l.value + r.value; 
}

そしてそれが機能する証拠:

int main() {
   int a = 0;
   a = a + a;
   Derived1 d11(0), d12(0);
   Derived2 d21(0), d22(0);
   add(d11, d12);
   add(d21, d22);
   add(d12, d22); // here it fails to compile...
}    
于 2012-10-05T09:28:04.140 に答える
-3

value基本クラスでのみ定義され、操作が派生メンバーにアクセスする必要がない限り、基本演算子を定義するだけで残りを暗黙的な型キャストに任せることができる場合があります。さまざまなタイプのエラーについては、列挙型ベースのシステムを使用してタイプを追跡し、単純な比較を行って無効な状態をチェックすることは、多少の犠牲を払う価値があるかもしれません。

enum eTypeEnum {BASE, DER1, DER2};

class Base {
public:
  virtual ~Base(){}

  Base(double value) : eType(BASE),value(value) {}
  Base(const Base& B) { value=B.value; }

  Base operator+ (const Base& B) const {
    if (eType != B.eType) return -1; //error condition
    return Base(value+B.value);
  }
  double getVal(){return value;}
protected:
  eTypeEnum eType;
  double value;
};

class Derived1 : public Base {
public:
  Derived1(double value) : Base(value) {eType = DER1;}
};

class Derived2 : public Base {
public:
  Derived2(double value) : Base(value) {eType = DER2;}
};


int main() {
  int tmp;
  Derived1 a(4.0);
  Derived2 b(3.0);
  Base c(2.0);

  cout << "aa:" << (a+a).getVal();     // 8
  cout << "\nbb:" << (b+b).getVal();   // 6
  cout << "\nba:" << (b+a).getVal();   // 7
  cout << "\nab:"<< (a+b).getVal();    // 7

  cout << "\ncc:"<< (c+c).getVal();    // 4
  cout << "\nac:"<< (a+c).getVal();    // 6
  cout << "\nbc:" << (b+c).getVal();   // 5
  cout << "\nabc:" << (a+b+c).getVal();// 9
  cout << endl;
  cin >> tmp;
  return 0;
}

出力: aa:8 bb:6 ba:-1 ab:-1 cc:4 ac:-1 bc:-1 abc:1

私が目にする唯一の問題は、複数の操作を連鎖させると、キャスティングが処理を台無しにすることです。ここで、a+b+c432 は、ビットがエラー状態 (-1 を返す) を経験する(a+b)+cように評価されますが、 '1' を返すことができる としてキャストされます。a+bBase(-1)+c

于 2012-10-05T09:02:18.933 に答える