2016-10-20 4 views
0

Я часто работаю с многомерными массивами, и я не являюсь большим поклонником std::vector, так как это не возможно создать экземпляр std::vector или std::vector из std::vector «s, используя ссылку без копирования базовых данных ,unique_ptr-х нулевого размера

Для одномерных массивов, я использую следующий

template<typename T> 
using deleted_aligned_array = std::unique_ptr<T[], std::function<void(T*)> >; 

template<typename T> 
deleted_aligned_array<T> deleted_aligned_array_create(size_t n) { 
    return deleted_aligned_array<T>((T*)_mm_malloc(n*sizeof(T),16), [](T* f)->void { _mm_free(f);}); 
} 

Это очень удобно и позволяет мне экземпляр динамически размеру массива, который также работает для нулевого размера. Кроме того, я могу использовать std::forward для передачи данных без копирования.

Для двумерного массива, я хотел бы сделать что-то вроде

template<typename T> 
using deleted_aligned_array2 = std::unique_ptr<T*,std::function<void(T**)>>; 

template<typename T> 
deleted_aligned_array2<T> deleted_aligned_array_create(size_t m, size_t n) { 
    auto arr = deleted_aligned_array2(new T*[m](), [&](T** x) { 
               if (malloc_usable_size(x) > 0) { 
               _mm_free(&(x[0][0])); 
               } 
               delete[] x;}); 
    if (m*n > 0) { 
    arr.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16); 

    // Row pointers 
    for (size_t iRow = 1; iRow < m; iRow++) { 
     (m_data.get())[iRow] = &(m_data.get()[0][iRow*n]); 
    } 
    } 
    return arr; 
} 

Он работает для массивов нулевого размера, но я получаю сообщение об ошибке от valgrind по понятным причинам, invalid read of size 8.

Возможно ли это решить изящно, без создания целого класса, содержащего элемент std::unique_ptr, где я реализую move-constructor, move-assign и т. Д. В конечном счете, я хотел бы обобщить это, чтобы использовать его для любых размер

template<typename T, size_t Dim> 
deleted_aligned_array<T,D> deleted_aligned_array_create(...); 

Возвращаемый массив должен быть уникальным указателем с указателем строки рекурсивно инициализирован и он должен поддерживать массивы нулевого размера, например,

auto arr = deleted_aligned_array_create<float,3>(4,5,10); 

должен возвращать 3-мерный массив с указателями строк и столбцов.

Вопросы: 1) Избегайте чтения недействительных данных простым способом. 2) Используйте параметр шаблона D для генерации типов: T*, T** и просто передайте D, чтобы код рекурсивно генерировал указатели строк (это у меня уже есть). 3) Предпочтительно переносным способом. malloc_usable_size является расширением GNU и называя его по x приводит к недопустимому чтению, когда размер 0.

Заранее спасибо

+0

* "STD :: вектор или зЬй :: вектор станд :: вектор, используя ссылку без копирования базовые данные "* Что вы имеете в виду? – Jarod42

+1

Хорошим способом реализации многомерного массива является сглаживание массива и вычисление индекса вручную. – Jarod42

+0

Если вы хотите использовать std :: vector в качестве типа аргумента для функции, вам необходимо создать экземпляр, и это вызовет копию. Я часто использую подмассивы многомерных массивов и хочу избежать копирования. –

ответ

0

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

template <class T, size_t D> 
class deleted_aligned_multi_array { 
}; 

template <class T> 
class deleted_aligned_multi_array<T,1> : public std::unique_ptr<T[], std::function<void(T*)> > { 
    deleted_aligned_multi_array(size_t n) : 
    std::unique_ptr<T[], std::function<void(T*)> >((T*)_mm_malloc(n*sizeof(T),16), 
                [](T* f)->void { _mm_free(f);}) {} 
}; 

template <class T> 
class deleted_aligned_multi_array<T,2> { 
public: 
    typedef T** pointer; 
    typedef std::unique_ptr<T*, std::function<void(T**)>> deleted_unique_array; 

    deleted_aligned_multi_array() : m(0), n(0), data() {} 

    deleted_aligned_multi_array(size_t m, size_t n) : m(m), n(n) { 
    if (m*n > 0) { 
     data = deleted_unique_array(new T*[m](), 
            [&](T** x) { 
             if (sps::msize(x) > 0) { 
             _mm_free(&(x[0][0])); 
             } 
             delete[] x;}); 

     data.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16); 

     for (size_t iRow = 1; iRow < m; iRow++) { 
     (data.get())[iRow] = &(data.get()[0][iRow*n]); 
     } 
    } 
    else { 
     data.reset(); 
    } 
    } 

    deleted_aligned_multi_array(deleted_aligned_multi_array&& other) : m(other.m), n(other.n), 
           data(std::move(other.data)) {} 

    deleted_aligned_multi_array& operator=(deleted_aligned_multi_array&& other) { 
    if (this != &other) { 
     data = std::move(other.data); 
     m = other.m; 
     m = other.n; 
    } 
    return *this; 
    } 

    T& operator()(size_t i, size_t j) { 
    return this->data.get()[0][i*n + j]; 
    } 

    T* operator[](size_t m) { 
    return &(data.get()[m][0]); 
    } 

    const T* operator[](size_t m) const { 
    return data.get()[m]; 
    } 

    pointer get() const { 
    return data.get(); 
    } 

    void reset(pointer __p = pointer()) { 
    data.reset(__p); 
    } 

    template<typename _Up> 
    void reset(_Up) = delete; 

private: 
    deleted_aligned_multi_array(const deleted_aligned_multi_array& other) = delete; 
    deleted_aligned_multi_array& operator=(const deleted_aligned_multi_array& a) = delete; 

public: 
    size_t m;     ///<Number of rows 
    size_t n;     ///<Number of columns 
    deleted_unique_array data; ///<Data 
}; 

Функция полезности для доступа к югу массив, теперь могут быть легко сделаны

template <class T> 
std::unique_ptr<T*, std::function<void(T*)> sub_array(size_t m, size_t n, size_t i, size_t j) { 
    // Establish row pointers with reference i and j and dimension mxn. 
}