Преобразование const float* в std::array‹float, ›

У меня есть const float*, указывающий на огромный массив, и я хотел бы иметь доступ к элементам через std::array. Как лучше всего это сделать? Без копирования элементов, если это возможно.

Спасибо!


person bayerb    schedule 26.03.2017    source источник
comment
Это невозможно без неопределенного поведения. Зачем тебе это?   -  person lisyarus    schedule 26.03.2017
comment
Мне нужно построить объект, и один из аргументов в его конструкторе — это std::array. И значения хранятся в RealVectorType (eigen.tuxfamily.org/dox/) Этот тип имеет метод data(), который возвращает float*.   -  person bayerb    schedule 26.03.2017
comment
Если конструктор класса принимает std::array, то размер должен быть известен во время компиляции — правильно? Можете ли вы показать код класса, который вы пытаетесь создать?   -  person G.M.    schedule 26.03.2017
comment
Этот объект, который вам нужно построить, имеет плохо спроектированный API, если он вынуждает вас использовать std::array. Можете ли вы поговорить с разработчиком, чтобы улучшить API, например. грамм. вместо этого принять пару итераторов?   -  person zett42    schedule 26.03.2017
comment
Да, я знаю размер во время компиляции. Это класс: pastebin.com/FU2vxWBS Я создал его, поэтому его можно изменить.   -  person bayerb    schedule 26.03.2017


Ответы (3)


Чтобы использовать std::array, вам нужно знать размер массива во время компиляции. Вы создаете пустой массив и используете std::copy для копирования элементов в массив.

Если код, который использует ваш const float*, знает этот размер только во время выполнения, вы не можете использовать std::array, но должны использовать std::vector. std::vector имеет конструктор, которому вы можете передать указатели на начало и конец диапазона для копирования в него.

Обратите внимание, что в обоих случаях контейнер владеет копией исходных элементов.

Без копирования элементов, если это возможно.

Нет, это невозможно. Стандартные контейнеры C++ предназначены для владения своим содержимым, а не просто для представления в них представления.

Вот пример, иллюстрирующий разницу:

#define SIZE 10 // let's assume some C or legacy code which uses macros

// ...

void f(const float* arr)
{
    // size is known at compile time
    std::array<float, SIZE> a;
    std::copy(arr, arr + SIZE, begin(a));
}

void g(const float* arr, int size)
{
    // size is only known at runtime
    std::vector<float> v(arr, arr + size);
}
person Christian Hackl    schedule 26.03.2017
comment
Макросы не являются устаревшей функцией. - person yeoman; 26.03.2017
comment
@yeoman: Использование макроса для определения размера контейнера в значительной степени является устаревшей функцией современного C++. Кроме того, в комментарии вообще не сказано, что каждое использование макроса означает устаревший код. - person Christian Hackl; 26.03.2017
comment
Это правда :) Но возможность использовать макросы для ведения журнала, функции, подобные утверждениям, обработку ошибок в контекстах без исключений и т. д. на самом деле является сильной языковой особенностью C++. Да, макросы C - тупые. Настоящие макросы, такие как LISP и RUST, были бы НАМНОГО предпочтительнее. Но наличие макросов вообще, хотя и в форме, которая требует от вас действительно думать о том, как будет анализироваться результат, когда вы их пишете, помогает избежать большого количества шаблонов, что в большинстве случаев является хорошей идеей :) - person yeoman; 26.03.2017
comment
Да, я действительно знаю размер. - person bayerb; 26.03.2017

Есть по крайней мере два предлагаемых дополнения к стандартной библиотеке, которые делают что-то подобное. Одним из них является std::experimental::to_array<T,N>(), который создает глубокую копию всех данных. Это расточительно с точки зрения времени и памяти, но может быть полезно, если вы действительно хотите создать копию, особенно const копию, которую вы не можете создать, а затем изменить.

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

Поскольку вы сказали, что можете изменить интерфейс конструктора, я настоятельно рекомендую вам это сделать. Если вы хотите сохранить версию, которая принимает std::array, вы можете делегировать ее версии, которая принимает произвольный диапазон данных, например:

#include <algorithm>
#include <array>
#include <iterator>
#include "span"

MyClass::MyClass( const span<value_type>& s /*, more, params */ )
{ 
 /* In this example, m_storage is some private container and span has a
  * container interface.
  */
  auto it = std::back_inserter(m_storage);
  std::copy_n( s.begin(), s.size(), it );
  // ...
}

MyClass::MyClass( std::array<value_type, initializer_size>& a /*, more, params */ )
: MyClass( span<value_type>( a.data(), a.size() ) /*, more, params */ )
{}
person Davislor    schedule 31.03.2017
comment
Кроме того, у Microsoft есть шаблон класса с именем array_view. - person Davislor; 02.04.2017

std::array не владеет указателями. Вы должны скопировать элементы. Используйте std::copy. Он работает с абстрактными итераторами, для которых также подходят указатели.

const float *source = ...; // C array of at least of size N
std::array<float, N> destination;
std::copy(source, source + N, destination.begin());

В стороне: без копирования элементов тип элемента любого контейнера с поддержкой владения указателем также должен быть const float. Или вам пришлось бы использовать const_cast‹>, если вы были уверены, что указатель на самом деле не является const float. Пожалуйста, используйте const_cast‹> только в тех случаях, когда это абсолютно неизбежно из-за недостатка во внешнем API :)

person yeoman    schedule 26.03.2017