C++ Как добавить очередь unique_ptr в вектор
Упрощенный код:
#include <queue>
#include <memory>
#include <vector>
class Foo {
public:
Foo() {};
virtual ~Foo() {}
};
int main()
{
std::queue<std::unique_ptr<Foo>> queue;
auto element = std::make_unique<Foo>();
queue.push(std::move(element));
std::vector<std::queue<std::unique_ptr<Foo>>> vector;
// Error 1
vector.push_back(queue);
// Error 2
vector.push_back(std::move(queue));
// Error 3
vector.push_back({});
return 0;
}
Ошибка:
'std:: unique_ptr>:: unique_ptr (const std:: unique_ptr<_Ty, std:: default_delete <_Ty >> &)': попытка сослаться на удаленную функцию
Очевидно, что копирование c_to r для unique_ptr удалено, но я не пытаюсь его скопировать. Я?
1 ответ
Это немного сложно. Все std::vector<T>
функции, которые могут увеличить размер вектора, должны делать это безопасным для исключения способом, если любая из этих двух вещей верна:
T
имеет конструктор перемещения, который гарантирует, что он никогда не вызовет никаких исключений; или же,T
имеет конструктор копирования.
Так что в большинстве реализаций, если T
объявлен конструктор перемещения nothrow
или эквивалент, vector
будет использовать конструктор перемещения T
для этих операций. Если нет, и T
имеет конструктор копирования, vector
будет использовать конструктор копирования, даже если T
имеет конструктор перемещения.
И проблема здесь в том, что std::queue
всегда объявляет, что имеет конструктор копирования, даже если этот конструктор копирования на самом деле не может быть создан, и всегда объявляет, что у него есть конструктор перемещения, который может генерировать, даже если конструктор перемещения члена контейнера гарантирует, что он не сгенерирует.
Стандарт определяет их в [queue.defn] как:
namespace std {
template<class T, class Container = deque<T>>
class queue {
// ...
public:
explicit queue(const Container&);
explicit queue(Container&& = Container());
// ...
};
}
Это определение шаблона класса может быть улучшено несколькими способами, чтобы быть более "дружественным к SFINAE" и избегать проблем, подобных той, с которой вы столкнулись. (Возможно, кто-то может проверить другие классы с похожими проблемами и представить предложение в рабочую группу библиотеки.)
Измените конструктор перемещения, чтобы он обещал не бросать, если
Container
type дает такое же обещание, обычно это делается с помощью языка вроде:explicit queue(Container&& rhs = Container()) nothrow(see below);
Замечания: выражение внутри
noexcept
эквивалентноis_nothrow_move_constructible_v<Container>
,Измените конструктор копирования, который будет удален, если
Container
Тип не копируется, как правило, с использованием языка, такого как:explicit queue(const Container&);
Примечания: этот конструктор должен быть определен как удаленный, если
is_copy_constructible_v<Container>
являетсяtrue
,