шаблон; Точка <2, двойная>; Точка <3, двойная> - PullRequest
0 голосов
/ 06 мая 2010

Я хочу создать свою собственную структуру Point, это только для целей изучения C ++.
У меня есть следующий код:

template <int dims, typename T>
struct Point {
    T X[dims];
    Point(){}
    Point( T X0, T X1 ) {
        X[0] = X0;
        X[1] = X1;
    }
    Point( T X0, T X1, T X2 ) {
        X[0] = X0;
        X[1] = X1;
        X[2] = X2;
    }
    Point<dims, int> toint() {
        //how to distinguish between 2D and 3D ???
        Point<dims, int> ret = Point<dims, int>( (int)X[0], (int)X[1]);
        return ret;
    }
    std::string str(){
        //how to distinguish between 2D and 3D ???
        std::stringstream s;
        s << "{ X0: " << X[0] << " | X1: " <<  X[1] << " }";
        return s.str();
    }
};
int main(void) {

    Point<2, double> p2d = Point<2, double>( 12.3, 45.6 );
    Point<3, double> p3d = Point<3, double>( 12.3, 45.6, 78.9 );

    Point<2, int> p2i = p2d.toint(); //OK
    Point<3, int> p3i = p3d.toint(); //m???

    std::cout << p2d.str() << std::endl; //OK
    std::cout << p3d.str() << std::endl; //m???
    std::cout << p2i.str() << std::endl; //m???
    std::cout << p3i.str() << std::endl; //m???

    char c; std::cin >> c;
    return 0;
}

из couse до сих пор вывод не то, что я хочу. мои вопросы: Как позаботиться о размерах Точки (2D или 3D) в функциях-членах Точки?

большое спасибо заранее
К сожалению

Ответы [ 6 ]

6 голосов
/ 06 мая 2010

Ваши размеры фиксируются во время компиляции с помощью шаблона arguemnt dims, поэтому вы можете выполнять их итерацию:

std::string str(){
    //how to distinguish between 2D and 3D ???
    std::stringstream s;
    s << "{ ";
    std::copy( X, X+dims, std::ostream_iterator<T>( s, "|") );
    s << " }";
    return s.str();    
}

Кроме того, вы можете предоставить конструктор в терминах dims:

Point( const T (&c) [dims] ){
  std::copy( c, c+dims, X );
}

Это позволяет использовать код, подобный double[] d={1,2,3}; Point<3,double> p(d);, который определяет количество аргументов в другом месте.

Для конструктора cast-to-of-ints вы просто можете использовать Point в качестве аргумента:

Point<dims,int> toint() const {
    Point<dims,int> result;
    std::copy( X, X+dims, result.X );
    return result;
}
3 голосов
/ 06 мая 2010

Вместо жесткого кодирования назначений, вы должны просто использовать цикл. Например:

std::string str(){
    if ( dims > 0 ){
        std::stringstream s;
        s << "{ X0: " << X[0];
        for ( int i = 1; i < dims; i++ ){
            s << " | X" << i << ": " <<  X[i];
        }
        s <<" }";
        return s.str();
    }
    return "{}";
}

Однако, с точки зрения вашего конструктора, вам придется либо специализировать свой шаблон, чтобы иметь конструкторы с правильным числом параметров, либо вам придется подождать, пока C ++ 0x будет использовать списки инициализатора, или просто приходится соглашаться на несколько менее мощный синтаксис, когда вы передаете в инициализирующем массиве. Однако для всех других функций вы можете просто использовать цикл for, используя dims для размеров точек. Я также должен отметить, что класс boost :: array может уже предоставлять необходимую вам функциональность, а если нет, то может быть целесообразно создать любую функциональность, которая вам нужна, поверх этого класса, поскольку она очень похожа на что у тебя есть.

Кроме того, кроме этого, вы должны предоставить типичную функцию оператора ostream << для вашего класса и дополнительно реализовать функцию str (), поскольку вы уже эффективно реализовали ее в своей реализации str (), но обеспечение такой перегрузки оператора является более гибким и мощным, чем просто предоставление str (). Возможно, вас заинтересует <a href="http://www.boost.org/doc/libs/1_42_0/libs/conversion/lexical_cast.htm" rel="nofollow noreferrer"> boost :: lexical_cast , так как он может преобразовать в строку любой объект, предоставляющий оператор ostream.

2 голосов
/ 06 мая 2010

Как уже говорили другие, на how to distinguish between 2D and 3D? отвечает простое использование dims в качестве переменной.

Более интересный вопрос - как заставить конструкторы вести себя так, чтобы можно было вызывать только правильный,Один из вариантов - boost::enable_if, который генерирует целенаправленную ошибку компилятора для существенной отмены объявления функции, если условие не выполняется.

template <int dims, typename T>
struct Point {
    T X[dims];
    Point(){} // should this really be empty?
    Point( typename enable_if_c< dims == 2, T >::type X0, T X1 ) {
        X[0] = X0;
        X[1] = X1;
    }
    Point( typename enable_if_c< dims == 3, T >::type X0, T X1, T X2 ) {
        X[0] = X0;
        X[1] = X1;
        X[2] = X2;
    }

Этот метод обычно называется SFINAE, дляОшибка замещения не является ошибкой (подтекст: компилятор может попробовать другую перегрузку), но в этом случае мы действительно хотим ошибку.

Другой альтернативой является предоставление типа переменной и аргумента по умолчанию для выборочного отключениятретий аргумент, или принудительно укажите его:

template< int dims, typename T >
class tweak_point_ctor_argument; // disallow general case of dims != 2,3

template< typename T >
class tweak_point_ctor_argument< 2, T > {
    tweak_point_ctor_argument() {} // private constructor
    typedef tweak_point_ctor_argument type;
    operator int() { return 0; }
    friend struct Point;
};

template< typename T >
class tweak_point_ctor_argument< 3, T > {
    typedef T type;
    friend struct Point;
};

    …
    Point( T X0, T X1, 
        typename tweak_point_ctor_argument< dims, T >::type X2
            = tweak_point_ctor_argument<dims, T>() ) {
        X[0] = X0;
        X[1] = X1;
        X[2] = X2;
    }

Если dims равен 2, ::type оценивается как tweak_point_ctor_argument< 2, T >, что не может быть построено, кроме как по умолчанию в аргументе.Таким образом, пользователь не может вызвать конструктор с третьим параметром.Если dims равен 3, ::type оценивается как T, в результате чего инициализатор аргумента по умолчанию становится недействительным, что вынуждает пользователя не использовать его.

Я не тестировал этот код, это простодемонстрация различных способов решения проблемы, enable_if является предпочтительным методом здесь.

1 голос
/ 06 мая 2010

Как насчет наличия одного класса для Point2D и одного для Point3D и включения этих классов в класс Point?

struct nil;

class PointBase {
 /* ... */
 PointBase();
 PointBase(const PointBase&);
 virtual ~PointBase();
}

template<class T_arg1, class T_arg2>
class Point2D : public PointBase {
 /* ... */
 Point2D(T_arg1 arg1, T_arg2 arg);
 Point2D(const Point2D& src) : PointBase(src) {}
 virtual ~Point3D();
};


template<class T_arg1, class T_arg2, class T_arg3>
class Point3D : public PointBase {
 /* ... */
 Point3D(T_arg1 arg1, T_arg2 arg2, T_arg3 arg3);
 Point3D(const Point3D& src) : PointBase(src) {}
 virtual ~Point3D();
};

template<class T_arg1, class T_arg2, class T_arg3 = nil>
class Point : public Point3D<T_arg1, T_arg2, T_arg3> {
 /* ... */
 Point(T_arg1 arg1, T_arg2 arg2, T_arg3 arg3)
  : Point3D<T_arg1, T_arg2, T_arg3>(arg1, arg2, arg3) {} 
 Point(const Point& src) : Point3D<T_arg1, T_arg2, T_arg3>(src) {}
 virtual ~Point();
}

template<class T_arg1, class T_arg2>
class Point<T_arg1, T_arg2, nil> : public Point2D<T_arg1, T_arg2> {
 /* ... */
 Point(T_arg1 arg1, T_arg2 arg2)
  : Point2D<T_arg1, T_arg2>(arg1, arg2) {} 
 Point(const Point& src) : Point2D<T_arg1, T_arg2>(src) {}
 virtual ~Point();
}

и затем используйте:

Point<double, double> my2DPoint(0.0, 0.0);
Point<double, double, double> my3DPoint(0.0, 0.0, 0.0);
1 голос
/ 06 мая 2010

Если вы хотите, чтобы ваши методы-члены знали измерение (ваш параметр шаблона "dims"), добавьте следующую строку в ваш шаблонный класс:

static const int Dimension = dims;

, тогда используйте Dimension в своих методах. Это статическая переменная-член const, поэтому компилятор сможет оптимизировать ее и не требовать хранения для нее.

Я только что заметил, что вы также можете использовать идентификатор "dims" непосредственно в функции-члене:

public void PrintDimensions()
{ 
     std::cout << dims;
}

отлично работает с моим компилятором.

1 голос
/ 06 мая 2010

Добавить переменную-член:

template <int dims, typename T>
struct Point {
    //whatever
    static const int Dimensions = dims;
};

теперь вы можете использовать его как в функциях-членах, так и во внешнем коде (если вы, конечно, не измените ограничения доступа).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...