Вопрос или проблема
Следующий код не компилируется на gcc 5.3 с ошибкой компилятора, которая жалуется на то, что конструктор копирования unique_ptr как-то вызывается. Мог бы кто-то объяснить, почему это происходит?
#include <iostream>
#include <memory>
#include <deque>
using Foo = std::deque<std::unique_ptr<int>>;
void foo() {
std::vector<Foo> a;
a.emplace_back(); // это не компилируется
}
Ключевая строка в сообщении об ошибке компилятора:
gcc-4.9.2/include/c++/4.9.2/bits/stl_construct.h:75:7: ошибка: использование удалённой функции ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [с _Tp = int; _Dp = std::default_delete<int>]’ { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
vector::emplace_back
должен обрабатывать перераспределение.-
при перераспределении
vector
, существующие элементы перемещаются, если один из следующих условий выполнен:- они не могут быть скопированы (как определяется с помощью
std::is_copy_constructible
); или - их конструктор перемещения
noexcept
.
в противном случае они копируются. Это необходимо для поддержания строгой гарантии безопасности при исключениях.
- они не могут быть скопированы (как определяется с помощью
- конструктор перемещения
std::deque<std::unique_ptr<int>>
не являетсяnoexcept
(в зависимости от реализации, он может потребовать выделения памяти). std::deque<std::unique_ptr<int>>
может быть скопирован согласноstd::is_copy_constructible
, поскольку это только проверяет наличие конструктора с правильной сигнатурой, а не то, будет ли тело данного конструктора на самом деле компилироваться.- поэтому
vector::emplace_back
пытается его скопировать. - компилятор выдает ошибку.
Ответ или решение
При компиляции кода, использующего вектор объектов std::deque<std::unique_ptr<int>>
, возникает ошибка, связанная с конструктором копирования std::unique_ptr
. Это можно объяснить несколькими важными моментами, касающимися особенностей обработки объектов в контейнерах C++ и работы с семантикой перемещения.
Проблема компиляции
Для начала, давайте взглянем на сам код:
#include <iostream>
#include <memory>
#include <deque>
using Foo = std::deque<std::unique_ptr<int>>;
void foo() {
std::vector<Foo> a;
a.emplace_back(); // эта строка вызывает ошибку компиляции
}
Когда вы вызываете emplace_back
для вектора a
, компилятор должен создать новый элемент Foo
, который фактически является std::deque<std::unique_ptr<int>>
. Однако, ввиду спецификации стандартной библиотеки и правил перемещения и копирования объектов, возникают следующие проблемы.
Причины возникновения ошибки
-
Перемещение и копирование вектора:
При добавлении нового элемента в вектор с помощьюemplace_back
, в случае если вектор нуждается в перераспределении памяти, он запускает процесс, который включает перемещение (или копирование) существующих объектов в новую память. -
Семантика перемещения:
std::deque
имеет конструктор перемещения, который не отмечен какnoexcept
, поскольку может выполнять операции, требующие выделения памяти. Это означает, что вектор не может гарантировать безопасное перемещение существующих элементов без исключений на этапе перераспределения. -
Копирование объектов:
Когда вектор пытается выполнить перемещение, он обращается к проверке, может ли он скопировать на случай, если перемещение не сработает.std::deque<std::unique_ptr<int>>
не может быть скопирован, потому чтоstd::unique_ptr
не имеет конструктора копирования (он удалён). Следовательно, компилятор обращается к копирующему конструктору, который фактически отсутствует, что и вызывает ошибку. -
Классовые проверки:
std::is_copy_constructible
подтверждает наличие конструктора с подходящей сигнатурой, но не проверяет, срабатывает ли он. Если объект не может быть скопирован из-за удалённого конструктора, тогда процесс копирования прерывается.
Заключение и рекомендации
В результате вышеизложенного, ошибка компиляции, с которой вы столкнулись, связана с тем, что вектор пытается создать копию или переместить объект std::deque
, содержащий std::unique_ptr
, что невозможно из-за отсутствия конструктора копирования.
Для решения данной проблемы вы можете рассмотреть следующие подходы:
- Избегайте использования
std::deque<std::unique_ptr<int>>
внутриstd::vector
. Вместо этого используйтеstd::vector<std::unique_ptr<int>>
напрямую. - Если ваша структура данных позволяет, возможно, стоит рассмотреть использование контейнеров, которые не требуют копирования (например, просто
std::vector
).
Это позволит вам обойтись без проблем с компиляцией и использовать преимущества современных возможностей C++ по безопасному управлению памятью.