Вложенные шаблоны и конструктор - PullRequest
0 голосов
/ 15 августа 2010

Редактировать : обратите внимание, что моя последняя цель - не работать с классом, а просто узнать больше о шаблонах: -)

Предположим, у вас есть шаблонный класс, который реализует вектор:

template <typename T>
class Vector
{
    public:
        Vector(size_t dim) {
            dimension = dim;
            elements = new T[dim];
        }
        /* Here more stuff, like operator[] etc... */
    private:
        size_t dimension;
        T * elements;
}

И предположим, что вы хотите построить матрицу из нее.Матрица - это просто вектор векторов, поэтому ее можно сконструировать следующим образом:

template <typename T>
class Matrix : public Vector<Vector<T> >
{
    /*...*/
}

И тут возникает проблема: в конструкторе мне нужно предоставить строки и столбцы в качестве параметра для внутренних векторов.Это должно быть что-то вроде

template <typename T>
Matrix<T>::Matrix (size_t ncols, size_t nrows)
         : Vector<Vector<T> > /* Here I need to specify size for both
                               * internal and external vectors */
{
}

Очевидно, я не могу написать Vector<Vector<T>(nrows)>(ncols), но это то, что мне нужно!

Возможное решение будет включать в себя размер внутри шаблона:

template <typename T, size_t N>
class Vector
{
    public:
        Vector() {
            elements = new T[N];
        }
        /* Here more stuff, like operator[] etc... */
    private:
        T * elements;
}

Следовательно, мне больше не нужны параметры конструктора, но это также вынуждает меня писать неуклюжий код с шаблонами везде (например, каждая функция, использующая Vector, должна быть объявлена ​​как

template <typename T, size_t N>
void foo (Vector<T,N> &vec) {...}

Есть ли у вас лучшие решения?

РЕДАКТИРОВАТЬ:

В качестве решения я черпал вдохновение из сообщений мистера Фуза и Чубсдада. Вот как я решил проблему:

/* The RowAccess class is just an array wrapper which raises an exception
 * if you go out of bounds */
template <typename T>
class RowAccess
{
    public:

        RowAccess (T * values, unsigned cols) : values(vals), cols(c) {}

        T & operator[] (unsigned i) throw (MatrixError) {
            if (i < cols) return values[i];
            else throw MatrixError("Column out of bound");
        }

    private:
        T * values;
        unsigned cols;
};

template <typename T>
class Matrix
{
    public:
        Matrix (unsigned rows, unsigned cols) {...}
        virtual ~Matrix () {...}

        RowAccess<T> operator[] (unsigned row) {
            if (row < rows) return RowAccess<T>(values + cols * row, cols);
            else throw MatrixError("Row out of boundary");
        }

    private:
        unsigned rows;
        unsigned cols;
        T * values;
};

Большое спасибо всем!

Ответы [ 4 ]

3 голосов
/ 15 августа 2010

В терминах ОО я бы проголосовал за «имеет» отношения между Матрицей и Вектором. Матрица имеет векторы, а матрица «является» вектором, что означает, что матрица не должна быть производной от вектора.

РЕДАКТИРОВАТЬ 1: небольшая коррекция. «... что означает, что Матрица не должна извлекать« публично »из Vector». Частное наследство может быть все еще в порядке.

2 голосов
/ 15 августа 2010

Использовать новое место размещения, как это (похоронено за вызовом uninitialized_fill)

template <typename T>
class Vector
{
    public:
        Vector(size_t dim, T const& c = T()) {
            dimension = dim;
            elements =
              static_cast<T*>(operator new(sizeof(T) * dim));
            std::uninitialized_fill(elements, elements + dim, c);
        }
        /* Here more stuff, like operator[] etc... */
    private:
        size_t dimension;
        T * elements;
};

Затем вы можете вызвать конструктор с помощью Matrix::Vector(ncols, Vector<T>(nrows)) (вам не нужно повторять аргумент для внешнего вектора, потому что Vector относится к Vector< Vector<T> > автоматически, так как вы наследуете от внешнего вектора. Вам нужно сделать Обязательно вызовите деструкторы вручную, прежде чем делать operator delete(elements) в деструкторе.

Возможно, вы также захотите встроить вектор в качестве члена, что я, вероятно, предпочел бы, потому что я думаю, что не обязательно все операции внешнего вектора имеют смысл для матрицы. Тогда инициализация выглядит как m(ncols, Vector<T>(nrows)).


Следует отметить, что std::vector также может использоваться для этого

template <typename T>
class Vector
{
    public:
        Vector(size_t dim, T const& c = T()):elements(dim, c)
        { }
    private:
        std::vector<T> elements;
};

Это простой и безопасный способ сделать это, и вы получаете автоматическое управление памятью.

1 голос
/ 15 августа 2010

Это не то, что вы просили, но есть большая вероятность, что матрица будет лучше реализована как один линейный вектор, где вы предоставите высокоуровневые методы доступа, которые выполняют индексацию (например, elmLoc = row * ncols + col),Таким образом, вам не нужно создавать и инициализировать вектор векторов.Вам также не нужно беспокоиться о случайном наличии некоторых внутренних векторов различного размера.Во всех реализациях с плотной матрицей, которые я когда-либо использовал, в качестве базовой реализации используется один линейный вектор.

0 голосов
/ 15 августа 2010

Это зависит от того, что вы ожидаете от своего класса Vector (и Matrix).

Либо вы хотите, чтобы размер определялся во время выполнения, и в этом случае я бы предложил добавить функцию resize(), которая позволила бы вам изменять размер вектора в конструкторе так, как вам нравится.

template <typename T>
class Vector
{
    public:
        Vector(size_t dim) {
            dimension = dim;
            elements = new T[dim];
        }
        Vector() : dimension(0), elements(0) {} // you need default constructor
        void resize(size_t dim) { // note: this could be implemented a lot better
          T* new_elements=new T[dim];
          for(int i=0; i<dim && i<dimension; i++)
            new_elements[i]=elements[i];
          delete [] elements;
          elements=new_elements; dimension=dim;
        }
        /* Here more stuff, like operator[] etc... */
    private:
        size_t dimension;
        T * elements;
}

Затем вы измените размер вашего Vectors в конструкторе Matrix в цикле.

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

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