Спецификации шаблона для конструктора n-мерного массива - PullRequest
3 голосов
/ 14 июля 2009

Я реализую класс n-мерного массива, который представляет собой шаблон следующим образом (обратите внимание, что данные хранятся в линейном массиве, длина которого является произведением всех измерений):

template< class valType, int rank >
class NDimensionalArray
{
public:

private:
    valType* m_data;
    int* m_dimensions;
    int m_rank;
};

Таким образом, идея заключается в том, что пользователь (я) может указать массив ранга 2 и определенного размера, то есть:

NDimensionalArray<double,2> matrix(10,10);

Теперь сложность заключается в том, чтобы специализировать конструкторы для 1-> n измерений, каждый конструктор принимает n параметров, где n - ранг массива. Теперь я подумал об использовании типа valarray, который используется в printf (), однако с этим определяем одномерный массив с двумя измерениями, то есть:

NDimensionalArray<double,1> matrix(10,10);

было бы вполне приемлемым поведением. Есть ли какая-то хитрая уловка, которую я могу использовать, чтобы компилятор делал повторения? Реально, пока я знаю ранг и длину каждого измерения, конструктор может быть общим:

{
    int nElements = m_dimensions[0];
    for ( int i=1 ; i<m_rank ; ++i )
        nElements *= m_dimensions[i];
    m_data = new valType[nElements];
}

Редактировать: Обратите внимание, что аналогичная операция будет необходима для методов доступа.

Также я рассмотрел вариант конструктора, который выглядит следующим образом:

NDimensionalArray( const NDimensionalArray<int,1>& dimensions );

Что можно использовать как:

NDimensionalArray<int,1> dimVec(2); // Need a specification for 1-dimensional arrays.
dimVec(0) = 10;
dimVec(1) = 10;
NDimensionalArray<double,2> matrix(dimVec);

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

Ответы [ 6 ]

7 голосов
/ 14 июля 2009

Хорошо, я играл с этим некоторое время. Вот некоторые шаблоны метапрограммирования, которые делают что-то близкое к тому, что вы хотите. Он позволяет вам указывать все измерения в строке, он не выполняет динамическое распределение памяти или другие подобные вещи. Кроме того, с хорошим компилятором C ++ (я тестировал с опцией VC ++ /O2), код будет полностью встроенным, без копий (фактически, для меня он встроил весь конструктор NDimensionalArray в точке вызова). Он будет полностью проверяться во время компиляции и не позволит вам пройти слишком мало или слишком много измерений. И это может быть повторно использовано для индексаторов. Здесь идет:

template<class T, int N>
class value_pack : private value_pack<T, N-1>
{
public:

    enum { size = N };

    value_pack(value_pack<T, N-1> head, const T& tail)
        : value_pack<T, N-1>(head)
        , value(tail)
    {
    }

    value_pack<T, N+1> operator() (const T& tail) const
    {
        return value_pack<T, N+1>(*this, tail);
    }

    template<int I>
    const T& get() const
    {
        return this->value_pack<T, I+1>::value;
    }

protected:

    const T value;
};

template<class T>
struct value_pack<T, 0>
{
};

struct
{
    template <class T>
    value_pack<T, 1> operator() (const T& tail) const
    {
        return value_pack<T, 1>(value_pack<T, 0>(), tail);
    }
} const values;


template <class ValType, int Rank>
struct NDimensionalArray
{
    NDimensionalArray(value_pack<ValType, Rank> values)
    {
        // ...
    }
};


int main()
{
    NDimensionalArray<int, 3> a(values(1)(2)(3));
}
2 голосов
/ 14 июля 2009

Я думаю, что лучшее решение - взять вектор целых и позволить конструктору проверить его по параметру шаблона 'rank'.

NDimensionalArray matrix(std::vector<int> matrixDimensions) 
{
    if (matrixDimensions.size() != rank) 
    {
        throw SomeException();
    }

    ...
}

Я не думаю, что какой-либо трюк с компилятором может быть альтернативой здесь. (За исключением perhps, использующего макросы, если вы можете что-то придумать, хотя это, строго говоря, не будет компилятором .)

1 голос
/ 14 июля 2009

Нет хорошего способа сделать это в C ++, как в настоящее время стандартизировано. В C ++ 0x вы сможете использовать пакеты параметров шаблона для аппроксимации (я думаю, что я правильно понял синтаксис, но не уверен насчет расширения в requires):

template <class ValType, int Rank>
struct NDimensionalArray
{
    template <class... Args>
    requires std::SameType<Args, ValType>... && std::True<sizeof...(Args) == Rank>
    NDimensionalArray(Args... values)
    {
        ...
    }
};
1 голос
/ 14 июля 2009

Вы можете взять массив std :: tr1 ::. Хмм:

#include <array>

template< class valType, int rank >
class NDimensionalArray
{
public:
   NDimensionalArray(const std::tr1::array<rank>& dims);
   // ...
};

NDimensionalArray<double,2> foo({10,10});

NDimensionalArray<double,2> bar({10}); // second dimension would be 0

NDimensionalArray<double,1> baz({10,10}); // compile error?

На самом деле я не уверен, работает ли это! Я прогоню его через комо.

Отредактировано Согласно комментариям, похоже, что этот подход будет выглядеть больше как:

std::tr1::array<2> dims = {10, 10};
NDimensionalArray<double,2> foo(dims);
1 голос
/ 14 июля 2009

Не прямой ответ, но посмотрите блиц-библиотеку.

0 голосов
/ 14 июля 2009

Boost имеет библиотеку multi-array , которая использует пользовательский объект для построения своего многомерного массива. Это действительно хороший способ сделать это; Я предлагаю вам изучить (или еще лучше, использовать) их код.

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