0

テンプレートを使用して単純な多次元 Point クラスを実装しようとしています (学習)。Point2D と Point3D の 2 つの特殊化が必要です。コンストラクターが Point p (1, 2) のように Point を直接初期化できるようにするために、これまでに得たものを次に示します。このコードはコンパイルして問題なく動作しますが、私が気に入らないのは特殊化のコードの繰り返し部分です。何か間違ったことをしているに違いありません。

私は C++ / テンプレートが初めてです - どんな助けでも大歓迎です。

#ifndef POINT_H_
#define POINT_H_

template< typename T, int Dimensions = 2 >
class Point
{
public:
    typedef typename T value_type;

    Point() { std::fill(elements_, elements_+Dimensions, 0); }
    Point(const Point<T, Dimensions>& rhs) : elements_(rhs.elements_) {}
    ~Point() {}


    Point & operator=(const Point<T, Dimensions>& rhs) { return *this; }

    const Point operator+(const Point<T, Dimensions>& p)
    {
        Point<T, Dimensions> ret;

        for(int i = 0; i < Dimensions; i++)
        {
            ret[i] += elements_[i] + p[i];
        }

        return ret;
    }

    Point & operator+=( const Point<T, Dimensions>& p)
    {
        for(int i = 0; i < Dimensions; i++)
        {
            elements_[i] += p[i];
        }

        return *this;
    }

    Point & operator-=( const Point<T, Dimensions> & p)
    {
        for(int i = 0; i < Dimensions; i++)
        {
            elements_[i] -= p[i];
        }

        return *this;
    }

    T & operator[](const size_t index)
    {
        return elements_[index];
    }

private:
    T elements_[Dimensions];
};

template<typename T>
class Point< T, 2 >
{
public:
    Point(const T x, const T y)
    {
        elements_[0] = x;
        elements_[1] = y;
    }

    typedef typename T value_type;

    Point() { std::fill(elements_, elements_+Dimensions, 0); }
    Point(const Point<T, 2>& rhs) : elements_(rhs.elements_) {}
    ~Point() {}


    Point & operator=(const Point<T, 2>& rhs) { return *this; }

    const Point operator+(const Point<T, 2>& p)
    {
        Point<T, 2> ret;

        for(int i = 0; i < 2; i++)
        {
            ret[i] += elements_[i] + p[i];
        }

        return ret;
    }

    Point & operator+=( const Point<T, 2>& p)
    {
        for(int i = 0; i < 2; i++)
        {
            elements_[i] += p[i];
        }

        return *this;
    }

    Point & operator-=( const Point<T, 2> & p)
    {
        for(int i = 0; i < 2; i++)
        {
            elements_[i] -= p[i];
        }

        return *this;
    }

    T & operator[](const size_t index)
    {
        return elements_[index];
    }

private:
    T elements_[2];
};


template< typename T>
class Point< T, 3 >
{
public:

    Point(const T x, const T y, const T z)
    {
        elements_[0] = x;
        elements_[1] = y;
        elements_[2] = z;
    }

    typedef typename T value_type;

    Point() { std::fill(elements_, elements_+3, 0); }
    Point(const Point<T, 3>& rhs) : elements_(rhs.elements_) {}
    ~Point() {}


    Point & operator=(const Point<T, 3>& rhs) { return *this; }

    const Point operator+(const Point<T, 3>& p)
    {
        Point<T, 3> ret;

        for(int i = 0; i < 3; i++)
        {
            ret[i] += elements_[i] + p[i];
        }

        return ret;
    }

    Point & operator+=( const Point<T, 3>& p)
    {
        for(int i = 0; i < 3; i++)
        {
            elements_[i] += p[i];
        }

        return *this;
    }

    Point & operator-=( const Point<T, 3> & p)
    {
        for(int i = 0; i < 3; i++)
        {
            elements_[i] -= p[i];
        }

        return *this;
    }

    T & operator[](const size_t index)
    {
        return elements_[index];
    }

private:
    T elements_[3];
};

typedef Point< int, 2 > Point2Di;
typedef Point< int, 3 > Point3Di;


#endif //POINT_H_
4

5 に答える 5

7

メイン テンプレートで 2D コンストラクターと 3D コンストラクターの両方を提供するだけです。

ここでは、解決すべき問題がないため、基本クラスやその他のRube Goldberg のソリューションにこだわる必要はありません。使用されていないものはすべて使用されていないテンプレート ランドにいます。

例:

#ifndef POINT_H_
#define POINT_H_

#include <array>            // std::array

#define STATIC_ASSERT( e ) static_assert( e, "!(" #e ")" )

template< typename T, int nDimensions = 2 >
class Point
{
private:
    std::array< T, nDimensions > elements_;

public:
    typedef T ValueType;

    T& operator[]( int const i )
    {
        return elements_[i];
    }

    T const& operator[]( int const i ) const
    {
        return elements_[i];
    }

    void operator+=( Point const& other )
    {
        for( int i = 0; i < nDimensions; ++i )
        {
            elements_[i] += other.elements_[i];
        }
    }

    void operator-=( Point const& other )
    {
        for( int i = 0; i < nDimensions; ++i )
        {
            elements_[i] -= other.elements_[i];
        }
    }

    friend Point operator+( Point const& a, Point const& b )
    {
        Point ret( a );

        ret += b;
        return ret;
    }

    friend Point operator-( Point const&a, Point const& b )
    {
        Point ret( a );

        ret -= b;
        return ret;
    }

    Point(): elements_() {}

    Point( int x, int y )
    {
        STATIC_ASSERT( nDimensions == 2 );
        elements_[0] = x;
        elements_[1] = y;
    }

    Point( int x, int y, int z )
    {
        STATIC_ASSERT( nDimensions == 3 );
        elements_[0] = x;
        elements_[1] = y;
        elements_[2] = z;
    }
};

typedef Point< int, 2 > Point2D;
typedef Point< int, 3 > Point3D;

#endif //POINT_H_

#include <iostream>
using namespace std;

wostream& operator<<( wostream& stream, Point3D const& point )
{
    return (stream << "(" << point[0] << ", " << point[1] << ", " << point[2] << ")");
}

int main()
{
    wcout << "starting" << endl;
    Point3D a( 1, 2, 3 );
    Point3D b( 4, 5, 6 );

    a += b;
    wcout << a << endl;
}
于 2012-08-09T20:57:34.250 に答える
3

あなたが示したものに似たクラステンプレートを持つことをお勧めします:

template< typename T, int Dimensions>
class Point {
};

さらに、それPoint2DPoint3D継承するクラス テンプレート:

template <typename T>
class Point2D : public Point<T,2>{
  // add X,Y constructor
};

template <typename T>
class Point3D : public Point<T,3>{
  // add X,Y, Z constructor
};

C++11 を使用している場合の別のオプションは、汎用Pointクラス テンプレートに可変個引数テンプレート コンストラクターを追加することです。これは継承を完全に回避し、任意の数の次元に対して一般的です。

template< typename T, unsigned int N>
class Point {
  template <typename ... Args>
  Point(const Args& ... args) : elements_{args...} {}
};

これにより、次のことが可能になります。

Point<int, 3> p3a(1,2,3); // OK, array values set to 1, 2, 3
Point<int, 3> p3b(1,2); // OK, array values set to 1, 2, 0
Point<int, 3> p3c(1,2,3,4); // compiler error! 
Point<double, 10> p10a(1,2,3,4,5,6,7,8,9,10); // OK
于 2012-08-09T20:45:33.853 に答える
1

jahhaj が言うように、共通コードを基本クラス テンプレートに移動し、そこから継承します。

template<typename T, int Dimensions>
class PointBase {
    ...  // all existing code from Point
};

template<typename T, int Dimensions = 2>
class Point: public PointBase<T, Dimensions> {
    // empty
};

template<typename T>
class Point<T, 2>: public PointBase<T, 2> {
public:
    Point(T x, T y): PointBase<T, 2>() { // convenience constructor
       (*this)[0] = x;
       (*this)[1] = y;
    }
};

余談ですが、C++11 の可変個引数テンプレートと初期化子リストにより、便利なコンストラクターを手動で作成する必要がなくなります。

于 2012-08-09T20:47:09.173 に答える
0

ルシアンの答えを試してみましたが、専門分野で新しいコンストラクターを提供するにはうまくいかないようです。

ただし、派生を使用して、型にさまざまなコンストラクターを提供することができます。残念ながら、この手法では、公開するすべてのコンストラクターを再定義する必要があります。

余談ですが、テンプレート化された型自体の定義内で現在の型を参照する場合、テンプレート パラメーターを繰り返す必要はないことに注意してください。

#ifndef POINT_H_ 
#define POINT_H_ 

template< typename T, int Dimensions = 2 > 
class Point 
{ 
public: 
    typedef typename T value_type; 

    Point() { std::fill(elements_, elements_+Dimensions, 0); } 
    Point(const Point& rhs) : elements_(rhs.elements_) {} 
    virtual ~Point() {} 


    Point & operator=(const Point& rhs) { return *this; } 

    Point operator+(const Point& p) 
    { 
        Point<T, Dimensions> ret; 

        for(int i = 0; i < Dimensions; i++) 
        { 
            ret[i] += elements_[i] + p[i]; 
        } 

        return ret; 
    } 

    Point & operator+=( const Point& p) 
    { 
        for(int i = 0; i < Dimensions; i++) 
        { 
            elements_[i] += p[i]; 
        } 

        return *this; 
    } 

    Point & operator-=( const Point& p) 
    { 
        for(int i = 0; i < Dimensions; i++) 
        { 
            elements_[i] -= p[i]; 
        } 

        return *this; 
    } 

    T & operator[](const size_t index) 
    { 
        return elements_[index]; 
    } 

private: 
    T elements_[Dimensions]; 
}; 


template<typename T>
class Point2D : public Point<T, 2>
{
public:

    Point2D()
    {
    }

    Point2D(const Point2D& rhs) : Point<T, 2>(rhs) 
    {
    }

    Point2D(const T x, const T y) 
    { 
        (*this)[0] = x; 
        (*this)[1] = y;
    }
};

template<typename T>
class Point3D : public Point<T, 3>
{
public:

    Point3D()
    {
    }

    Point3D(const Point3D& rhs) : Point<T, 3>(rhs) 
    {
    }

    Point3D(const T x, const T y, const T z) 
    { 
        (*this)[0] = x; 
        (*this)[1] = y;
        (*this)[2] = z;
    }
};


typedef Point2D< int > Point2Di; 
typedef Point3D< int > Point3Di; 



#endif //POINT_H_ 

衒学的な点として、基本クラスにも const インデックス アクセサーを追加することをお勧めします。

    const T & operator[](const size_t index) const
    { 
        return elements_[index]; 
    } 
于 2012-08-09T20:45:52.013 に答える
0

コンストラクターの入力引数の数を変更したいので、2 つの方法があります
。1) すべての実装を基本クラスに移動し、そこからクラスを駆動してから、駆動されるクラスを特殊化します
。2) プリプロセッサ コマンドを使用してコンストラクターを繰り返します(たとえば、 )を使用してから、テンプレート (または)BOOST_PP_REPEATを使用して無効なコンストラクターを無効にします。 最初のテクニックは非常に単純ですが、2 番目のテクニックについては、多くのドキュメントと「disable_if」に関するヘルプがあります。tr1::disable_ifboost::disabled_if

BOOST_PP_REPEAT

于 2012-08-09T20:48:58.920 に答える