Std :: list <std :: unique_ptr <T>>: passing it
Let's say I have std::list of class T :
std::list<T> l; When passing it to functions, I would use the link:
someFunction( std::list<T> &l ) What is the best way to go through (elements) of std::list from unique_ptr s?
std::list< std::unique_ptr<T> > l; Like this:
someFunction( std::unique_ptr<T> ptr ) Or that:
someFunction( T* ptr ) Or that:
someFunction( T &ref ) And how can I call this with the std::list back() function, for example? This is IMHO the "equivalent", but I'm sure something is missing here.
thanks
In order of best to worse:
- someFunction (const T &);
- SomeFunction (T &);
- someFunction (const std :: unique_ptr <T> &);
- SomeFunction (stand :: unique_ptr <T> &);
The first one is the best, because it does not change the object and will not work with the object no matter how you select it (for example, you can switch to shared_ptr without problems).
Number two will work no matter which smart pointer you use; however, it assumes that you can modify the object, and whenever you can do something const, you should.
Numbers 3 and 4 both allow the object to be directed towards a mutation; however, # 3 does not allow you to change the smart pointer, and number 4 does not. Both have the disadvantage that they force the use of unique_ptr, while the two above they will work regardless of the smart pointer class.
Passing a unique_ptr value by value, as in some other examples, is not an option; unique_ptr must be unique. If you copy it, consider using shared_ptr.
For the first two, if you called it on the result of back (), it would look like this:
someFunction(*(lst.back())); // dereference lst.back() before passing it in. For the last two, if you called it when returning back (), it would look like this:
someFunction(lst.back()); // pass the smart pointer, not the object to // which the smart pointer currently points. Make unique_ptr not passed by value, first of all it will not compile without std::move , and if you do use std::move , it will empty the value that you saved in your list , and you can no longer access it .
This is due to the fact that unique_ptr not copied, it does not have a copy constructor of type unique_ptr::unique_ptr(const unique_ptr<T>& other) , but only has a move constructor ( unique_ptr::unique_ptr(unique_ptr<T>&& source) ).
unique_ptr, as well as classes / instances containing unique_ptr, can be used in std :: list (and other containers) provided that they have a class_name(class_name &&) move constructor defined (which, of course, has a class_name(class_name &&) ).
When you pass these elements, you always move (and not copy) them, so always use std :: move () on lvalues, e.g. inmy_list.push_back(std::move(my_element));
this makes it visible that you are passing (= moving) an element to the list and that my_element is "empty" (for example, empty unique_ptr) after this operation.
Example:
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; // ... }