Любая альтернатива std:: dynarray в настоящее время доступна?
С++ 11 дал нам отличный std::array
, который требует, чтобы размер был известен во время компиляции:
std::array<int, 3> myarray = {1, 2, 3};
Теперь у меня есть старые буферы short*
для переноса, размер которых будет известен (и это будет, конечно) только во время выполнения.
С++ 14 будет определять std::dynarray
для покрытия этого случая, но dynarray
пока недоступен в GCC 4.7 или в Clang 3.2.
Итак, кто-нибудь знает контейнер, который сопоставим с std::array
(с точки зрения эффективности), но не требует указания размера во время компиляции? Я подозреваю, что у Boost есть что-то готовое для меня, хотя я ничего не мог найти.
Ответы
Ответ 1
Вы можете (ab) использовать std::valarray<short>
.
int main() {
short* raw_array = (short*) malloc(12 * sizeof(short));
size_t length = 12;
for (size_t i = 0; i < length; ++ i) {
raw_array[i] = (short) i;
}
// ...
std::valarray<short> dyn_array (raw_array, length);
for (short elem : dyn_array) {
std::cout << elem << std::endl;
}
// ...
free(raw_array);
}
valarray
поддерживает большинство функций dynarray
, за исключением:
- Распределитель
- обратный итератор
-
.at()
-
.data()
Обратите внимание, что стандарт (по состоянию на n3690) не требует непрерывного хранения valarray
, хотя нет причин не делать этого:).
(Для некоторой детали реализации в libstdС++ она реализована как пара (длина, данные), а в libС++ реализована как (начало, конец).)
Ответ 2
Я думаю, что std::vector
- это то, что вы ищете, прежде чем dynarray
станет доступным. Просто используйте конструктор выделения или reserve
, и вы избежите лишних затрат на перераспределение.
Ответ 3
Положите голосование на std::unique_ptr<short[]>(new short[n])
, если вам не нужен доступ к диапазону доступа, предоставляемый std::dynarray<T>::at()
. Вы даже можете использовать список инициализаторов:
#include <iostream>
#include <memory>
int main(int argc, char** argv) {
const size_t n = 3;
std::unique_ptr<short[]> myarray(new short[n]{ 1, 2, 3 });
for (size_t i = 0; i < n; ++i)
std::cout << myarray[i] << '\n';
}
Ответ 4
Буфер и размер, а также некоторые базовые методы дают вам большую часть того, что вы хотите.
Множество шаблонов, но что-то вроде этого:
template<typename T>
struct fixed_buffer {
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
std::size_t length;
std::unique_ptr<T[]> buffer;
std::size_t size() const { return length; }
iterator begin() { return data(); }
const_iterator begin() const { return data(); }
const_iterator cbegin() const { return data(); }
iterator end() { return data()+size(); }
const_iterator end() const { return data()+size(); }
const_iterator cend() const { return data()+size(); }
reverse_iterator rbegin() { return {end()}; }
const_reverse_iterator rbegin() const { return {end()}; }
const_reverse_iterator crbegin() const { return {end()}; }
reverse_iterator rend() { return {begin()}; }
const_reverse_iterator rend() const { return {begin()}; }
const_reverse_iterator crend() const { return {begin()}; }
T& front() { return *begin(); }
T const& front() const { return *begin(); }
T& back() { return *(begin()+size()-1); }
T const& back() const { return *(begin()+size()-1); }
T* data() { return buffer.get(); }
T const* data() const { return buffer.get(); }
T& operator[]( std::size_t i ) { return data()[i]; }
T const& operator[]( std::size_t i ) const { return data()[i]; }
fixed_buffer& operator=(fixed_buffer &&) = default;
fixed_buffer(fixed_buffer &&) = default;
explicit fixed_buffer(std::size_t N):length(N), buffer( new T[length] ) {}
fixed_buffer():length(0), buffer() {}
fixed_buffer(fixed_buffer const& o):length(o.N), buffer( new T[length] )
{
std::copy( o.begin(), o.end(), begin() );
}
fixed_buffer& operator=(fixed_buffer const& o)
{
std::unique_ptr<T[]> tmp( new T[o.length] );
std::copy( o.begin(), o.end(), tmp.get() );
length = o.length;
buffer = std::move(tmp);
return *this;
}
};
at()
отсутствует, как и распределители.
operator=
отличается от предложения dyn_array
- блоков предложений operator=
, я даю ему семантику значений. Несколько методов менее эффективны (например, конструкция copy
). Я разрешаю пустое fixed_buffer
.
Это, вероятно, блокирует возможность использования стека для хранения dyn_array
, что, вероятно, не позволяет его. Просто удалите мой operator=
и тривиальный конструктор, если вы хотите поведение более близкое к dyn_array.
Ответ 5
С++ 14 также добавляет массивы переменной длины, аналогичные тем, что на C99, и которые уже поддерживаются некоторыми компиляторами:
void foo(int n) {
int data[n];
// ...
}
Это не контейнер, поскольку он не поддерживает begin()
и end()
и т.д., но может быть работоспособным решением.
Ответ 6
dynarray
очень легко реализовать себя без компонента распределения стека, что, по-видимому, невозможно сделать до тех пор, пока, возможно, С++ 14 в любом случае, поэтому я просто перевернул обратный-обратный dynarray
(forwardport?) как часть моей библиотеки и начал использовать ее с тех пор. Работает на С++ 03 без каких-либо положений "пустоты в Небраске", так как это не совсем зависит от каких-либо специфических возможностей С++ 11, и он опрятен иметь
Таким образом, когда С++ 1y/2z dynarray
приходит, мой код по-прежнему по большей части совместим.
(Это также одно из многих очевидных "почему у С++ не было этого раньше?", так что хорошо иметь его вокруг).
Это было до того, как я узнал, что, по-видимому, С++ 1y- dynarray
и С++ 1y-runtime-size-массивы - это одно и то же предложение (одно просто синтаксическое сахара для другого), а не два разных, но -комплементарные предложения, как я впервые подумал. Поэтому, если бы мне пришлось решить тот же вопрос в настоящее время, я бы, вероятно, переключился на что-то, основанное на правильном решении @Yakk.