std::список< стандартный::unique_ptr<T> >:передавая его по кругу

StackOverflow https://stackoverflow.com/questions/3222926

  •  13-09-2020
  •  | 
  •  

Вопрос

Скажем, у меня есть std::list от class T'с:

std::list<T> l;

При передаче его в функции я бы использовал ссылку:

someFunction( std::list<T> &l )

Каков наилучший способ передачи (элементов) std::list от unique_ptrс?

std::list< std::unique_ptr<T> >  l;

Подобный этому:

someFunction( std::unique_ptr<T> ptr )

Или это:

someFunction( T* ptr )

Или это:

someFunction( T &ref )

И что, как бы я назвал это, используя std::listback() функция, например?ИМХО, все это "своего рода" эквиваленты, но я уверен, что здесь я что-то упускаю.

Спасибо

Это было полезно?

Решение

В порядке от лучшего к худшему:

  1. Некоторая функция(const T&);
  2. Некоторая функция(T&);
  3. Некоторая функция(const std::unique_ptr<T>&);
  4. Некоторая функция(std::unique_ptr<T>&);

Первый вариант является лучшим, потому что он не изменяет объект it и будет работать с объектом независимо от того, как вы его выделили (например, вы могли бы переключиться на shared_ptr без проблем).

Номер два также будет работать независимо от того, какой интеллектуальный указатель вы используете;однако предполагается, что вы можете изменить объект, и всякий раз, когда вы можете сделать что-то постоянным, вы должны это сделать.

Числа 3 и 4 позволяют изменять объект, на который указывают;однако #3 не позволяет изменять интеллектуальный указатель, в то время как номер 4 позволяет.Оба имеют тот недостаток, что они вынуждают использовать unique_ptr, тогда как два приведенных выше варианта будут работать независимо от класса интеллектуального указателя.

Передача unique_ptr по значению, как у вас есть в некоторых других примерах, не является вариантом;предполагается, что unique_ptr должен быть уникальным.Если вы копируете его, рассмотрите возможность использования shared_ptr.

Для первых двух, если бы вы вызвали его по результату back(), это выглядело бы как:

someFunction(*(lst.back()));  // dereference lst.back() before passing it in.

Для последних двух, если бы вы вызвали его при повторном вызове back(), это выглядело бы как:

someFunction(lst.back()); // pass the smart pointer, not the object to
                          // which the smart pointer currently points.

Другие советы

Делать нет проходить unique_ptr по значению, во-первых, он не будет компилироваться без std::move и если ты делать использовать std::move это приведет к удалению значения, которое вы сохранили в своем list и вы больше не сможете получить к нему доступ.

Это потому что unique_ptr не поддается копированию, у него нет конструктора копирования типа unique_ptr::unique_ptr(const unique_ptr<T>& other) вместо этого у него есть только конструктор перемещения (unique_ptr::unique_ptr(unique_ptr<T>&& source)).

unique_ptr, а также классы / экземпляры, содержащие unique_ptr, могут использоваться в std::list (и других контейнерах) при условии, что они имеют конструктор перемещения class_name(class_name &&) определен (который, конечно же, есть у unique_ptr).

Когда вы передаете эти элементы, вы всегда перемещаете (а не копируете) их, поэтому вы всегда используете std::move() для значений lvalues, как в
my_list.push_back(std::move(my_element));
это делает видимым, что вы передаете (= перемещаете) элемент в список, и что my_element является "пустым" (например, пустым unique_ptr) после этой операции.

Пример:

typedef std::unique_ptr<uint8_t[]> data_ptr;

class data_holder
{
private:
    int something_about_data;
    data_ptr data;
public:
    data_holder(data_ptr && _data)
        : data(std::move(_data))
    {}

    // hey compiler, please generate a default move constructor for me
    // despite of present destructor
    data_holder(data_holder &&) = default;

    ~data_holder()
    {
        // ...
    }

    bool is_empty() const { return ! bool(data); }
}

// ...
{
    // ...
    data_holder the_element(data_ptr(new uint8_t[DATA_SIZE]));

    // move the_element into the_list
    the_list.push_back(std::move(the_element));
    if (the_element.is_empty())
        std::cerr << "data_holder 'the_element' is now empty!" << std::endl;
    // ...
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top