4

これは設計上の問題です。

テンプレート クラスがあり、テンプレートの種類に応じてメソッドを追加したいと考えています。DRY 原則を実践するために、私は次のパターンを考え出しました (定義は意図的に省略されています)。

template <class T>
class BaseVector: public boost::array<T, 3>
{
protected:
    BaseVector<T>(const T x, const T y, const T z);
public:
    bool operator == (const Vector<T> &other) const;
    Vector<T> operator + (const Vector<T> &other) const;    
    Vector<T> operator - (const Vector<T> &other) const;
    Vector<T> &operator += (const Vector<T> &other)
    {
        (*this)[0] += other[0];
        (*this)[1] += other[1];
        (*this)[2] += other[2];

        return *dynamic_cast<Vector<T> * const>(this);
    }

    virtual ~BaseVector<T>()
    {
    }
}

template <class T>
class Vector : public BaseVector<T>
{
public:
    Vector<T>(const T x, const T y, const T z)
    : BaseVector<T>(x, y, z)
    {
    }
};

template <>
class Vector<double> : public BaseVector<double>
{
public:
    Vector<double>(const double x, const double y, const double z);
    Vector<double>(const Vector<int> &other);
    double norm() const;
};

BaseVector は単なる実装の詳細に過ぎないつもりです。これは機能しますが、私は心配していoperator+=ます。私の質問は:thisポインターの動的キャストはコードの匂いですか? 私がやろうとしていることを達成するためのより良い方法はありますか (コードの重複やユーザーコードでの不要なキャストを避けます)? それとも、BaseVector コンストラクターはプライベートなので安全ですか?

編集:

申し訳ありませんが、はい、仮想 dtor を持っていますが、貼り付けるのを忘れていました。コードはそれなしではコンパイルされません。

4

5 に答える 5

5

別のアプローチを検討することをお勧めします (以下の例では、別のアプローチを示すために必要な最小限のコードに単純化されていることに注意してください)。

まず、Curiously Recurring Template Parameter (CRTP) について考えてみましょう。

template <typename T, typename DerivedVector>
struct BaseVector
{
    DerivedVector& operator+=(DerivedVector const& other)
    {
        return static_cast<DerivedVector&>(*this);
    }
};

template <typename T>
struct Vector : BaseVector<T, Vector<T>>
{
};

派生型が何であるかを常に知っているので、astatic_castで十分です。がVector<T>ベースである唯一のクラスでありBaseVector<T>、パラメーターが常に同じであることが保証されている場合T、厳密に言えば、CRTP パラメーターは不要です。派生型が何であるかを常に知っているため、astatic_castは安全です。

または、演算子はメンバー関数である必要はないため、非メンバー演算子関数テンプレートを宣言できます。

template <typename T, typename DerivedVector>
struct BaseVector
{
};

template <typename T>
struct Vector : BaseVector<T, Vector<T>>
{
};

template <typename T>
Vector<T>& operator+=(Vector<T>& self, Vector<T> const& other)
{
    return self;
}

後者はオペレーターにとって望ましいものですが、通常のオペレーター以外のメンバー関数では機能しないため、CRTP アプローチが望ましいでしょう。

于 2012-07-02T21:54:41.553 に答える
4

はい、技術的な観点からは問題ありません。ただし、 がdynamic_cast機能するには、基本クラスが多態的である必要があります。したがって、少なくともデストラクタ (純粋な) を仮想化する必要があります。

また、代わりにそれを追加したい:

// potential null dereference
return *dynamic_cast<Vector<T> * const>(this);

次のように書く方が安全です。

// potential std::bad_cast exception
return dynamic_cast<Vector<T> & const>(*this);

元の質問に答えるには:

私がやろうとしていることを達成するためのより良い方法はありますか (コードの重複やユーザーコードでの不要なキャストを避けます)?

はい。詳細については、静的ポリモーフィズム奇妙な繰り返しテンプレート パターン、およびポリシー ベースのクラス設計についてお読みください。

于 2012-07-02T21:13:37.003 に答える
3

あなたの方法はどれもvirtual. 仮想メソッドがなければ、コンパイラは実行時の型情報を追加しません。RTTIdynamic_castがないと機能しません。

于 2012-07-02T21:13:05.647 に答える
1

これを達成するためのより良い方法は、 pimplイディオムだと思います。あなたが言うように、BaseVectorは単なる実装の詳細です。そのため、クラスのクライアントはそれを知らないはずです(これにより、自由に変更できます)。

この場合、これはBaseVectorから継承するのではなく、BaseVectorをVector 含む必要があります。次に、独自の算術代入演算子を定義し、BaseVectorに転送します。

実装の詳細にはまだ1つのバージョンしかなく、それらはに存在するため、これはDRYに違反しませんBaseVector。インターフェイスを繰り返すことはまったく問題ありません。

于 2012-07-02T21:19:08.253 に答える