1

私は型階層を持っていますが、実装するためのクリーンで良い方法がわかりませoperator<operator==

基本的に、私はすでにこれを持っています:

class Parent {
    public:
        virtual ~Parent() {}
};

class A : public Parent { int         data; };
class B : public Parent { double      data; };
class C : public Parent { std::string data; };

bool operator==(A const & lhs, A const & rhs) { return lhs.data == rhs.data; }
bool operator< (A const & lhs, A const & rhs) { return lhs.data <  rhs.data; }

bool operator==(B const & lhs, B const & rhs) { return lhs.data == rhs.data; }
bool operator< (B const & lhs, B const & rhs) { return lhs.data <  rhs.data; }

bool operator==(C const & lhs, C const & rhs) { return lhs.data == rhs.data; }
bool operator< (C const & lhs, C const & rhs) { return lhs.data <  rhs.data; }

私も実装したいのはこれです:

bool operator==(Parent const & lhs, Parent const & rhs) { ... }
bool operator< (Parent const & lhs, Parent const & rhs) { ... }

私は現在、次のようにして実装しています。

bool operator==(Parent const & lhs, Parent const & rhs) {
    try {
        return dynamic_cast<A const &>(lhs) == dynamic_cast<A const &>(rhs);
    } catch(std::bad_cast const & e) {
    }

    try {
        return dynamic_cast<B const &>(lhs) == dynamic_cast<B const &>(rhs);
    } catch(std::bad_cast const & e) {
    }

    try {
        return dynamic_cast<C const &>(lhs) == dynamic_cast<C const &>(rhs);
    } catch(std::bad_cast const & e) {
    }

    assert(typeid(lhs) != typeid(rhs));
    return false;
}

しかし、これはひどいようです。これについてよりクリーンな方法はありますか?

4

2 に答える 2

1

複雑なタイプの比較には、DoubleDispatchが役立つ場合があります。

タイプが非常に単純な場合は、すべてを1つにまとめると効果的な場合があります。3つの符号なしバリアントの例では、1つのタイプを使用してすべてのサイズに対応し、動的ディスパッチやタイプのより複雑なグラフを回避する方がよいでしょう。


元の質問に適用されます。ここで、A、B、およびCはすべて符号なしタイプを使用しました。

まあ、1つの迅速で汚いアプローチは次のようになります:

class Parent {
protected:
  virtual ~Parent() {}
public:
  bool operator<(const Parent& pOther) const {
    return this->as_uint64() < pOther.as_uint64();
  }
  // ...
private:
  // using a type which accommodates all values
  virtual uint64_t as_uint64() const = 0;
};

そして、から派生するParentのは次の形式になります。

class A : public Parent {
// ...
private:
    virtual uint64_t as_uint64() const { return this->data; }
private:
    uint16_t data;
};

次にParent、すべてのコンパレータを定義するだけで、すべてのParentタイプが比較可能になります。

于 2012-08-19T03:05:55.537 に答える
0

dynamic_castシングルディスパッチと型キャストには仮想コンパレータを使用します。

class ABC_base {
public:
    virtual ~ABC_base() {} 
    bool operator < (ABC_base const & rhs) const {
        return this->comparator(rhs) < 0;
    }
protected:
    virtual int comparator (ABC_base const &) = 0;
};

class ABC : public ABC_base {
protected:
    virtual int comparator(ABC_base const & rhs) const {
        try {
            return my_comparator(dynamic_cast<ABC const&>(rhs));
         // Run-time cast failed - use double dispatch as fallback
         } catch (std::bad_cast&) {
             return -rhs.comparator(*this);
         }
     }
private:
    int my_comparator(ABC const & rhs) const {
        if (data < rhs.data)
            return -1;
        if (data == rhs.data)
            return 0;
        if (data > rhs.data)
            return 1;
    }
    T data;
};

コードの仕組みは次のとおりです。

基本クラスのoperator <が呼び出され、動的ルックアップを使用してを検索しますcomparator。戻り値をチェックして、それよりも小さいかどうかを確認します。

派生クラスのコンパレータは、派生クラスのメンバーで比較を実行できるように、基本クラス参照をダウンキャストしようとします。

派生クラス参照を使用する代わりに、なぜ基本クラス参照を使用するのですか? 関数のシグネチャが正しくないため、仮想ディスパッチは機能しません。

ダウンキャストが成功すると、非仮想プライベートコンパレータが呼び出されます。それ以外の場合は、仮想ディスパッチを再度使用し(rhs ? *this)て結果を無効にし、逆の順序を補正します。

1つの仮想関数でキャストと比較を行ってみませんか?関数はキャストと比較の2つのことを行うため、コードが煩雑になります。したがって、プライベートコンパレータ機能があります。の行に沿って、派生クラスで基本関数を使用する場合はclass ABC_der : public ABC、を呼び出しますABC::comparator(static_cast<ABC const&>(rhs))Base::強制静的ディスパッチを使用すると、ヘルパー比較関数を公開する必要がなくなります。

現在、this同じrhsタイプなので、最終的に実際の比較を行うことができます。ステートメントのチェーンは、JavaおよびCのセマンティクスifに準拠した値を返すために使用されます。Comparableqsort()

于 2012-08-19T03:26:03.240 に答える