C ++ Как добавить очередь unique_ptr в вектор - PullRequest
0 голосов
/ 05 июня 2018

Упрощенный код:

#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>> &) ': попытка сослаться на удаленную функцию

Очевидно, что копирование файла unique_ptr удалено, но я не пытаюсь его скопировать.Я?

1 Ответ

0 голосов
/ 05 июня 2018

Это немного сложно.Все 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 делаетто же самое обещание, обычно выполняемое на языке, таком как:

    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> is true.

...