Я пытаюсь выделить массив объектов, которые не являются ни копируемыми, ни назначаемыми, ни имеют конструктор по умолчанию. У объектов есть аргументы, которые определяются во время выполнения. Я знаю, что вы можете решить эту проблему, имея массив указателей или умно используя новые размещения, но меня больше интересует, возможно ли это сделать чисто с помощью магии C ++ 11 (1y). Поэтому, пожалуйста, это чисто теоретический интерес, поэтому избегайте попыток решить «мою проблему», предлагая обходные пути.
…Итак, вопрос: есть ли способ заставить следующую работу работать в C ++ 11 или C ++ 14:
class X{
public:
explicit X(int a){...}
X(const X&) = delete;
void operator = (const X&) = delete;
private:
...
};
class Y{
public:
Y(const std::vector<int>& args) {
x = new X[]{args};
}
~Y(){
delete [] x;
}
private:
X* x;
};
В частности, я ищу решение / конструкцию, которая соответствует следующим критериям:
Я забыл упомянуть, что конструктор перемещения недоступен. В данном случае, X
порождает рабочий поток и выполняется в контексте this
для первоначально созданного объекта любая попытка использовать конструктор перемещения повредит состояние выполняющегося потока.
Класс, который не является ни копируемым, ни подвижным, ни имеет конструктор по умолчанию, не может храниться в стандартном контейнере (не соответствует требованиям) или распределении массива переменного размера (которое позволяет указывать аргументы только для фиксированного числа элементов) ,
Это означает, что вам нужно вместо этого выделить необработанную память и использовать размещение new для создания объектов. Вы можете обернуть это в векторный класс с фиксированным пространством.
template <typename T>
class fixed_capacity_vector {
public:
using size_type = std::size_t;
fixed_capacity_vector(size_type capacity)
: data_(::operator new(capacity * sizeof(T)), size_(), capacity_(capacity)
{}
fixed_capacity_vector(const fixed_capacity_vector&) = delete;
fixed_capacity_vector(fixed_capacity_vector&&) = delete;
fixed_capacity_vector& operator =(const fixed_capacity_vector&) = delete;
fixed_capacity_vector& operator =(fixed_capacity_vector&&) = delete;
~fixed_capacity_vector() {
for (size_type i = 0; i < size_; ++i) data_[i].~T();
::operator delete(data_);
}
template <typename... Args>
T& emplace_back(Args&&... args) {
if (size_ == capacity_) throw out_of_range();
new (data_ + size_) T(std::forward<Args>(args)...);
++size_;
return data_[size_-1];
}
private:
T* data_;
size_type size_;
size_type capacity_;
};
Ты можешь использовать std::vector
И его emplace_back
функционировать, если вы делаете X
по крайней мере подвижный.
class X{
public:
explicit X(int){}
X(X&&) = default;
X(const X&) = delete;
void operator = (const X&) = delete;
};
int main() {
std::vector<X> xs;
xs.emplace_back(0);
xs.emplace_back(1);
xs.emplace_back(2);
xs.emplace_back(3);
}
(Если вы объявляете конструктор копирования, даже если это объявление удаляет его, компилятор не будет автоматически генерировать какие-либо специальные элементы перемещения, поэтому вам необходимо явно запросить их)
Это в основном сводится к стратегии «массив с размещением нового», но все абстрагируется в понятия высокого уровня.
Если вы не можете использовать подвижный тип, вы должны реализовать вектороподобный класс, который предварительно выделяет память и никогда не перераспределяет. Ничего подобного в стандартной библиотеке нет.
Вам придется следить за созданными элементами вручную, но вы можете использовать allocator
помогать:
class Y{
public:
Y(const std::vector<int>& args):
alloc{},
n{args.size()},
x{alloc.allocate(n)}
{
auto end = x;
try {
for (auto arg: args)
alloc.construct(end++, arg);
} catch (...) {
while (end != x)
alloc.destroy(--end);
alloc.deallocate(x, n);
throw;
}
}
~Y() {
for (auto end = std::next(x, n); end != x; --end)
alloc.destroy(end);
alloc.deallocate(x, n);
}
private:
std::allocator<X> alloc;
const std::size_t n;
const X *x;
};