массивы — контейнер C ++ с не копируемым типом неподвижных элементов

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

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

Есть ли простой способ реализовать это без накладных расходов на выделение памяти и косвенного использования std::vector<std::unique_ptr<T>>?

3

Решение

Вот простое, но неполное решение в предположении, что каждый элемент построен с одинаковыми аргументами. Оно использует размещение new построить элементы на месте (см. также этот ТАК вопрос):

#include <cstdlib>
#include <utility>
#include <new>

// sample structure, non-copyable, non-moveable, non-default-constructible
struct Foo
{
Foo() = delete;
Foo(const Foo&) = delete;
Foo& operator = (const Foo&) = delete;
Foo(Foo&&) = delete;
Foo& operator = (Foo&&) = delete;

Foo(int a, char b, double c) : m_a(a), m_b(b), m_c(c) { }

int m_a;
char m_b;
double m_c;
};

template <typename T>
struct MyArray
{
// Array ctor constructs all elements in-place using the
// provided parameters
template <typename... Args>
MyArray(std::size_t sz, Args&&... args)
: m_sz(sz),
m_data(static_cast<T*>(malloc(sz * sizeof(T))))
{
for (std::size_t i=0; i<m_sz; ++i)
{
new (&m_data[i]) T(std::forward<Args>(args)...);
}
}

~MyArray()
{
for (std::size_t i=0; i<m_sz; ++i)
{
m_data[i].~T();
}
free(m_data);
}

std::size_t m_sz;
T *m_data;
};

int main()
{
Foo foo(1, '2', 3.0);
std::size_t s = 5;
MyArray<Foo> foo_arr(s, 1, '2', 3.0);
}

Обратите внимание, что некоторые вещи отсутствуют:

  • Эта базовая реализация приведет к утечке памяти, если внутри нее возникнет исключение MyArrayконструктор.
  • Вы, вероятно, захотите реализацию итератора, begin()/end() операторы и т. д., для большего удобства и для получения того же поведения, что предусмотрено стандартными контейнерами.
  • Для иллюстрации я также не беспокоился о правильной инкапсуляции. Вы, вероятно, должны сделать m_sz а также m_data частные участники.
2

Другие решения

Msgstr «Есть ли простой способ реализовать это без накладных расходов на выделение памяти и косвенного обращения, связанных с использованием std :: vector>?»

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

1

Поскольку элементы, которыми вы хотите управлять, не являются ни перемещаемыми, ни копируемыми, контейнер может содержать только указатели на элементы. Не зная больше или вашего требования, трудно догадаться, являются ли сырые указатели или std::unique_ptr было бы более уместным.

Для контейнера фиксированного размера std::array может быть уместным. К сожалению, размер должен быть выражением времени компиляции.

Альтернативой может быть использование необработанного массива и размещение новых для создания элементов на месте. Вы можете попытаться использовать вектор вместо необработанного массива, поскольку вектор использует непрерывную память, как обычный массив, но я не могу представить, как вы можете использовать его для хранения не копируемых, не перемещаемых и не конструируемых по умолчанию объектов.

Между прочим, построение массива на конструктивных объектах не по умолчанию с размером, не известным во время компиляции в C ++, не так уж тривиально. Единственный способ, который я мог найти, — это создать массив символов правильного размера, объявить указатель на класс, указывающий на начало массива, а затем создать элементы с размещением new:

char buf = new char[n * sizeof(X)];
X* x = reinterpret_cast<X*>(buf);
for(int i=-1; i<n i++) {
new (x + i) X(i);  // or whatever appropriate ctor...
}
0
По вопросам рекламы [email protected]