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" и избегать проблем, подобных той, с которой вы столкнулись. (Возможно, кто-то может проверить другие классы с похожими проблемами и представить предложение в рабочую группу библиотеки.)

  1. Измените конструктор перемещения, чтобы он обещал не бросать, если Container type дает такое же обещание, обычно это делается с помощью языка вроде:

    explicit queue(Container&& rhs = Container()) nothrow(see below);
    

    Замечания: выражение внутри noexcept эквивалентно is_­nothrow_­move_­constructible_­v<Container>,

  2. Измените конструктор копирования, который будет удален, если Container Тип не копируется, как правило, с использованием языка, такого как:

    explicit queue(const Container&);
    

    Примечания: этот конструктор должен быть определен как удаленный, если is_­copy_­constructible_­v<Container> является true,

Другие вопросы по тегам