16

私はテンプレートベクトルタイプで簡単な数学ライブラリを書いています:

template<typename T, size_t N>
class Vector {
    public:
        Vector<T, N> &operator+=(Vector<T, N> const &other);
        // ... more operators, functions ...
};

今、私はこれらのいくつかのために特別にいくつかの追加機能が必要です。関数を使用して特定の座標にアクセスしたいx()y()します。Vector<T, 2>これのために部分的な特殊化を作成することができます:

template<typename T>
class Vector<T, 3> {
    public:
        Vector<T, 3> &operator+=(Vector<T, 3> const &other);
        // ... and again all the operators and functions ...
        T x() const;
        T y() const;
};

しかし今、私は汎用テンプレートにすでに存在するすべてを繰り返しています。

継承も使用できます。汎用テンプレートの名前をに変更するとVectorBase、次のようになります。

template<typename T, size_t N>
class Vector : public VectorBase<T, N> {
};

template<typename T>
class Vector<T, 3> : public VectorBase<T, 3> {
    public:
        T x() const;
        T y() const;
};

ただし、問題は、すべての演算子がで定義されているため、インスタンスVectorBaseを返すことです。これらを変数VectorBaseに割り当てることはできません。Vector

Vector<float, 3> v;
Vector<float, 3> w;
w = 5 * v; // error: no conversion from VectorBase<float, 3> to Vector<float, 3>

Vectorこれを可能にするために、暗黙の変換コンストラクターを与えることができます。

template<typename T, size_t N>
class Vector : public VectorBase<T, N> {
    public:
        Vector(VectorBase<T, N> const &other);
};

しかし、今、私はからVectorVectorBase、そしてまた戻って変換しています。型はメモリ内で同じであり、コンパイラはこれをすべて最適化する可能性がありますが、それは不格好に感じられ、本質的にコンパイル時の問題である潜在的な実行時のオーバーヘッドはあまりありません。

これを解決する他の方法はありますか?

4

4 に答える 4

11

CRTPを使用してこの問題を解決できると思います。このイディオムは、boost::operatorで使用されます。

template<typename ChildT, typename T, int N>
class VectorBase 
{    
public:
    /* use static_cast if necessary as we know that 'ChildT' is a 'VectorBase' */
    friend ChildT operator*(double lhs, ChildT const &rhs) { /* */ }
    friend ChildT operator*(ChildT const &lhs, double rhs) { /* */ }
};

template<typename T, size_t N>
class Vector : public VectorBase<Vector<T,N>, T, N> 
{
};

template<typename T>
class Vector<T, 3> : public VectorBase<Vector<T, 3>, T, 3>
{
public:
    T x() const {}
    T y() const {}
};

void test()
{
    Vector<float, 3> v;
    Vector<float, 3> w;
    w = 5 * v;
    w = v * 5;
    v.x();

    Vector<float, 5> y;
    Vector<float, 5> z;
    y = 5 * z;
    y = z * 5;
    //z.x(); // Error !!
}
于 2010-05-09T16:48:15.280 に答える
4

しばらく前にC++0xの機能を試してみたときに思いついたことがあります。これで使用される唯一のC++0x機能はstatic_assertであるため、Boostを使用してこれを置き換えることができます。

基本的に、静的サイズチェック関数を使用して、特定のインデックスがベクトルのサイズよりも小さいことを確認することができます。インデックスが範囲外の場合、静的アサートを使用してコンパイラエラーを生成します。

template <std::size_t Index> 
void size_check_lt() const 
{ 
    static_assert(Index < N, "the index is not within the range of the vector"); 
}

次にget()、指定されたインデックスの要素への参照を返すメソッドを提供できます(明らかに、constオーバーロードも役立ちます)。

template <std::size_t Index> 
T& get()
{ 
    size_check_lt<Index>(); return data_[Index]; 
}

次に、次のような単純なアクセサーを記述できます。

T& x() { return get<0>(); }
T& y() { return get<1>(); }
T& z() { return get<2>(); }

ベクトルに要素が2つしかない場合は、xとyを使用できますが、zは使用できません。ベクトルに3つ以上の要素がある場合は、3つすべてを使用できます。

コンストラクターに対して同じことを行うことになりました。次元2、3、および4のベクトルに対してコンストラクターを作成しsize_check_eq、それぞれ次元2、3、および4のベクトルに対してのみインスタンス化できるようにするaを追加しました。興味があれば、今夜家に帰ったら、完全なコードを投稿してみることができます。

プロジェクトを途中でやめたので、この方法で実行すると、遭遇しなかった大きな問題が発生する可能性があります...少なくとも検討するオプションです。

于 2010-05-03T12:22:33.157 に答える
0

最も簡単な方法は?外部関数の使用:

template <class T>
T& x(Vector<T,2>& vector) { return vector.at<0>(); }

template <class T>
T const& x(Vector<T,2> const& vector) { return vector.at<0>(); }

テンプレートプログラミングでは、外部関数を使用するのが、機能を追加する最も簡単な方法です。これは、発生した特殊化の問題のためです。

一方、スコープを制限するための機能を提供したり、/機能をx使用yしたりするzこともできます。Nenable_ifdisable_if

于 2010-05-03T12:23:34.357 に答える
0

代入演算子の入力の問題を回避できるかどうかはわかりませんが、さまざまな演算子のテンプレートバージョン、それらを実装するためのヘルパー関数を定義し、継承を使用することで、作業を少し楽にすることができます。

template <typename T, std::size_t N>
class fixed_array {
public:
    virtual ~fixed_array() {}
    template <std::size_t K>
    fixed_array& operator+=(fixed_array<T,K> const& other) {
        for (std::size_t i=0; i<N; ++i)
            this->contents[i] += other[i];
        return *this;
    }
    template <std::size_t K>
    fixed_array& operator=(fixed_array<T,K> const& other) {
        assign_from(other);
        return *this;
    }
    T& operator[](std::size_t idx) {
        if (idx >= N)
            throw std::runtime_error("invalid index in fixed_array[]");
        return contents[idx];
    }
protected:
    template <std::size_t K>
    void assign_from(fixed_array<T,K> const& other) {
        for (std::size_t i=0; i<N; ++i)
            this->contents[i] = other[i];
    }
private:
    T contents[N];
};

template <typename T>
class fixed_2d_array: public fixed_array<T,2> {
public:
    T x_coord() const { return (*this)[0]; }
    T y_coord() const { return (*this)[1]; }
    template <std::size_t K>
    fixed_2d_array& operator=(fixed_array<T,K> const& other) {
        assign_from(other);
        return *this;
    }
};

int
main() {
    fixed_array<int,5> ary1;
    fixed_2d_array<int> ary2;
    ary2 = ary1;
    ary1 = ary2;
    ary2 += ary1;
    ary1 += ary2;
    return 0;
}
于 2010-05-03T12:44:22.157 に答える