std::list< std::unique_ptr<T> >: passing it around - c++

Say I have a std::list of class T's:
std::list<T> l;
When passing it into functions, I would use a reference:
someFunction( std::list<T> &l )
What is the best way to pass around (the elements) of a std::list of unique_ptrs?
std::list< std::unique_ptr<T> > l;
Like this:
someFunction( std::unique_ptr<T> ptr )
Or this:
someFunction( T* ptr )
Or this:
someFunction( T &ref )
And what how would I call it using the std::list's back() function for example? These are IMHO all "kind of" equivalent, but I'm sure I'm missing something here.
Thanks

In order of best to worse:
someFunction(const T&);
someFunction(T&);
someFunction(const std::unique_ptr<T>&);
someFunction(std::unique_ptr<T>&);
The first one is the best because it does not modify the object it and it will work with the object no matter how you have allocated it (for example, you could switch to shared_ptr with no problems).
Number two will also work regardless of what smart pointer you are using; however, it assumes that you can modify the object, and whenever you can make something const, you should.
Numbers 3 and 4 both allow the object being pointed-to to be mutated; however, #3 does not allow the smart pointer to be modified, while number 4 does. Both have the disadvantage that they force the use of unique_ptr, whereas the two above it would work regardless of smart pointer class.
Passing a unique_ptr by value, as you have in some of the other examples is not an option; a unique_ptr is supposed to be unique. If you are copying it, consider using shared_ptr.
For the first two, if you invoked it on the result of back(), it would look like:
someFunction(*(lst.back())); // dereference lst.back() before passing it in.
For the latter two, if you invoked it on the resut of back(), it would look like:
someFunction(lst.back()); // pass the smart pointer, not the object to
// which the smart pointer currently points.

Do not pass unique_ptr by value, first of all it won't compile without a std::move and if you do use std::move it will empty the value you have stored in your list and you won't be able to access it any more.
This is because unique_ptr is not copyable, it doesn't have a copy constructor of type unique_ptr::unique_ptr(const unique_ptr<T>& other) instead it only has a move constructor (unique_ptr::unique_ptr(unique_ptr<T>&& source)).

unique_ptr and also classes / instances containing unique_ptr can be used in std::list (and other containers), provided that they have move constructor class_name(class_name &&) defined (which unique_ptr, of course, has).
When you pass around those elements, you're always moving (and not copying) them, so you always use std::move() on lvalues, like in
my_list.push_back(std::move(my_element));
this makes visible that you're passing (= moving) the element into the list, and that my_element is "empty" (like empty unique_ptr) after that 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;
// ...
}

Related

C++ std::vector difference between creating object then adding it vs creating it inside the vector?

Since std::vector::push_back(obj) creates a copy of the object, would it be more efficient to create it within the push_back() call than beforehand?
struct foo {
int val;
std::string str;
foo(int _val, std::string _str) :
val(_val), str(_str) {}
};
int main() {
std::vector<foo> list;
std::string str("hi");
int val = 2;
list.push_back(foo(val,str));
return 0;
}
// or
int main() {
std::vector<foo> list;
std::string str("hi");
int val = 2;
foo f(val,str);
list.push_back(f);
return 0;
}
list.push_back(foo(val,str));
asks for a foo object to be constructed, and then passed into the vector. So both approaches are similar in that regard.
However—with this approach a c++11 compiler will treat the foo object as a "temporary" value (rvalue) and will use the void vector::push_back(T&&) function instead of the void vector::push_back(const T&) one, and that's indeed to be faster in most situations. You could also get this behavior with a previously declared object with:
foo f(val,str);
list.push_back(std::move(f));
Also, note that (in c++11) you can do directly:
list.emplace_back(val, str);
It's actually somewhat involved. For starters, we should note that std::vector::push_back is overloaded on the two reference types:
void push_back( const T& value );
void push_back( T&& value );
The first overload is invoked when we pass an lvalue to push_back, because only an lvalue reference type can bind to an lvalue, like f in your second version. And in the same fashion, only an rvalue reference can bind to an rvalue like in your first version.
Does it make a difference? Only if your type benefits from move semantics. You didn't provide any copy or move operation, so the compiler is going to implicitly define them for you. And they are going to copy/move each member respectively. Because std::string (of which you have a member) actually does benefit from being moved if the string is very long, you might see better performance if you choose not to create a named object and instead pass an rvalue.
But if your type doesn't benefit from move semantics, you'll see no difference whatsoever. So on the whole, it's safe to say that you lose nothing, and can gain plenty by "creating the object at the call".
Having said all that, we mustn't forget that a vector supports another insertion method. You can forward the arguments for foo's constructor directly into the vector via a call to std::vector::emplace_back. That one will avoid any intermediate foo objects, even the temporary in the call to push_back, and will create the target foo directly at the storage the vector intends to provide for it. So emplace_back may often be the best choice.
You ‘d better use
emplace_back(foo(val,str))
if you are about creating and pushing new element to your vector. So you perform an in-place construction.
If you’ve already created your object and you are sure you will never use it alone for another instruction, then you can do
push_back(std::move(f))
In that case your f object is dangled and his content is owned by your vector.

Is there any valid reason for which a method should take a R-value reference as input?

As expressed in the title, I was wondering if there is a valid reason/example for which a given class method, excluding move constructor and move assignment operator, or free-function should take, as input parameter, a R-value reference.
Avoiding unnecessary copies when retaining values. I like to refer to this functions as "sink functions". This occurs very often when defining setters.
class A
{
private:
std::string _s;
public:
void setS(const std::string& s) { _s = s; }
void setS(std::string&& s) { _s = std::move(s); }
};
int main()
{
A a;
std::string s{"some long string ......."};
a.setS(s); // copies and retains `s`
a.setS(std::move(s)); // moves and retains `s`
}
There are two ways to model a sink argument.
The first is by taking by-value:
void foo(std::string);
the second is by rvalue reference:
void foo(std::string&&);
with the possible variant of including a const& overload to simplify work for the caller.
inline void foo(std::string const& s){
auto tmp = s;
return foo(std::move(tmp));
}
The take-sink-by-value has an extra overhead of a single std::move over taking it by && and const& (or requiring caller to manually copy a non-temporary value and move it in themselves). It doesn't require the 2nd overload.
So if that one move is worth accounting for, taking sink arguments by const& and && can save you a move. Plus, if copy is extra expensive, you can make it awkward at the call site and thus discourage it.
But that isn't the only reason. Sometimes you want to detect if something is an rvalue or lvalue, and only copy if it is an rvalue.
As an example, suppose we had a range adapter backwards. backwards takes an appropriate range (something you can for(:) over, and whose iterators can be reversed) and returns a range that iterates over it backwards.
Naively, all you have to do is get begin and end from your source range, then make reverse iterators and store them and return them from your own begin and end methods.
Sadly, this breaks:
std::vector<int> get_some_ints();
for( int x : backwards( get_some_ints() ) ) {
std::cout << x << "\n";
}
because the lifetime of the temporary returned from get_some_ints is not extended by the for(:) loop!
That for(:) expands to roughly:
{
auto&& __range_expression = backwards( get_some_ints() );
auto __it = std::begin( __range_expression );
auto __end = std::end( __range_expression );
for (; __it != __end; ++__it) {
int x = *__it;
std::cout << x << "\n";
}
}
(There are some small lies told to children above, but it is close enough for this discussion).
In particular this line:
auto&& __range_expression = backwards( get_some_ints() );
the return value of backwards is lifetime extended; but the lifetime of its arguments are not!
So if backwards takes a R const&, the vector is silently destroyed prior to the loop, and the iterators involved are invalid.
So backwards must store a copy of the vector for the above code to be valid. That is our only opportunity to make the vector last long enough!
On the other hand, in a more conventional case:
auto some_ints = get_some_ints();
for( int x : backwards( some_ints ) ) {
std::cout << x << "\n";
}
storing an extra copy of some_ints would be a horrid idea and quite unexpected.
So in this case, backwards needs to detect if its argument is an rvalue or an lvalue, and if it is an rvalue it needs to copy it and store it in the return value, and if it is an lvalue it needs to either just store iterators or a reference to it.
Sometimes you want to take ownership of something large like a std::vector but you want to avoid making a copy by accident.
By only providing an r-value reference overload a caller who wants to pass a copy has to do so explicitly:
class DataHolder {
std::vector<double> a;
std::vector<int> b;
public:
DataHolder(std::vector<double>&& a, std::vector<int>&& b) : a(a), b(b) {}
};
auto a1 = makeLotsDoubles();
auto b1 = makeLotsInts();
DataHolder holder(std::move(a1), std::move(b1)); // No copies. Good.
auto a2 = makeLotsDoubles();
auto b2 = makeLotsInts();
DataHolder holder(a2, b2) // Forgot to move, compiler error.
If instead, you had used pass-by-value then if you forget to use std::move on an lvalue then a copy is made.
It's useful to transfer an uncopyable object to the class. Some objects have no copy constructor, which means they can't be passed by value. One sees this with RAII, where creating a copy would acquire a new set of resources. Or in general, when the copy constructor and/or destructor have side effects outside the object being (de)constructed. Making a copy might not be merely inefficient, like copying a std::string, but quite impossible.
One way to pass an object like this is to pass it as a pointer. For example:
// Creates a file on construction, deletes on destruction
class File;
{
// Create files
File* logfile1 = new File("name1");
File logfile2("name2");
// Give files to logger to use
logger.add_output(logfile1); // OK
logger.add_output(&logfile2); // BAD!
}
File has no copy constructor, as a copy would create and delete the same file as the original, which makes no sense. So we pass a pointer to the file to the logger object to avoid copying. With logfile1 it works ok, assuming of course that logger deletes the File when it's done with it. But logfile2 has two big problems. One is that logger can't delete it since it wasn't allocated with new. The other is that logfile2 will be destructed, deleting the file and invalidating the pointer to it saved in logger, when logfile2 leaves scope at the end of the block.
We could give File a move constructor, which would transfer ownership of the file to the destination File and make the source File "empty". Now we can write the above code in a way that works.
{
// Create files
File* logfile1 = new File("name1");
File logfile2("name2");
// Give files to logger to use
logger.add_output(std::move(*logfile1));
logger.add_output(std::move(logfile2));
logger.add_output(File("name3"));
delete logfile1;
}
We can now create a File with new, or as a local object, or even as an unnamed pr-value. The use of std::move also makes it clear, at the call site, that ownership of the File is transferred to logger. With the pointer ownership transfer isn't clearly shown, one depends on logger.add_output() documenting the semantics and checking that documentation.

Can I get away with putting auto_ptr in a STL container?

I am inheriting an interface, and implementing a virtual function that is supposed to do some work on a list of dynamically allocated objects. The first step is to remove duplicates from the list based on some custom equivalence criteria:
class Foo { /* ... */ };
struct FooLess
{
bool operator()(const Foo *lhs, const Foo *rhs);
}
struct FooEqual
{
bool operator()(const Foo *lhs, const Foo *rhs);
}
void doStuff(std::list<Foo*> &foos)
{
// use the sort + unique idiom to find and erase duplicates
FooLess less;
FooEqual equal;
foos.sort( foos.begin(), foos.end(), less );
foos.erase(
std::unique( foos.begin(), foos.end(), equal ),
foos.end() ); // memory leak!
}
The problem is that using sort + unique doesn't clean up the memory, and the elements to be erased have unspecified values after unique, so I cannot perform the cleanup myself before eraseing. I was considering something like this:
void doStuff(std::list<Foo*> &foos)
{
// make a temporary copy of the input as a list of auto_ptr's
std::list<auto_ptr<Foo>> auto_foos;
for (std::list<Foo>::iterator it = foos.begin(); it != foos.end(); ++it)
auto_foos.push_back(auto_ptr<Foo>(*it));
foos.clear();
FooLess less; // would need to change implementation to work on auto_ptr<Foo>
FooEqual equal; // likewise
auto_foos.sort( auto_foos.begin(), auto_foos.end(), less );
auto_foos.erase(
std::unique( auto_foos.begin(), auto_foos.end(), equal ),
auto_foos.end() ); // okay now, duplicates deallocated
// transfer ownership of the remaining objects back
for (std::list<auto_ptr<Foo>>::iterator it = auto_foos.begin();
it != auto_foos.end(); ++it)
{ foos.push_back(it->get()); it->release(); }
}
Will this be okay, or am I missing something?
I am not able to use C++11 (Boost might be possible) or change the function signature to accept a list of straightforward Foos.
To put an object into a standard container the object needs value semantics (the standard says "copy assignable" and "copy constructable"). Among other things, that means the copy constructor and assignment operator needs to create a copy of an object (leaving the original intact)
The auto_ptr copy constructor does not do that. Instead, the copy constructor and assignment operator transfer ownership of the pointer.
As a consequence, it is not possible for a standard container to contain an auto_ptr.
A lot of implementations (as in compiler and standard library) have the standard containers and/or auto_ptr coded so attempting to have a container of auto_ptr's will trigger a compiler error. Unfortunately, not all implementations do that.
There are generally the following methods you can use in C++98:
Define some pointer that will do what std::auto_ptr can't do. There was an old version of that thing, which contained an additional field of type bool that marked ownership. It was marked mutable, so it could be modified also in the object being read from when copying. The object was deleted at the end only if owned was true. Something like:
==
template <class T> class owning_ptr
{
T* ptr;
mutable bool owns;
public:
void operator =(T* src) { ptr = src; owns = true; }
owning_ptr(const owning_ptr& other)
{
// copy the pointer, but STEAL ownership!
ptr = other.ptr; owns = other.owns; other.owns = false;
}
T* release() { owns = false; return ptr; }
~owning_ptr() { if ( owns ) delete ptr; }
/* ... some lacking stuff ..*/
};
You may try out boost::shared_ptr
Instead of std::unique, you may try to do std::adjacent_find in a loop. Then you'll just find all elements that are "the same" as by your equal. If there's more than one element, you will erase them in place (you are allowed to do it because it's a list, so iterators remain valid).

Copy on write proper usage?

I'm tyring to understand how COW works, I found following class on wikibooks, but I don't understand this code.
template <class T>
class CowPtr
{
public:
typedef boost::shared_ptr<T> RefPtr;
private:
RefPtr m_sp;
void detach()
{
T* tmp = m_sp.get();
if( !( tmp == 0 || m_sp.unique() ) ) {
m_sp = RefPtr( new T( *tmp ) );
}
}
public:
CowPtr(T* t)
: m_sp(t)
{}
CowPtr(const RefPtr& refptr)
: m_sp(refptr)
{}
CowPtr(const CowPtr& cowptr)
: m_sp(cowptr.m_sp)
{}
CowPtr& operator=(const CowPtr& rhs)
{
m_sp = rhs.m_sp; // no need to check for self-assignment with boost::shared_ptr
return *this;
}
const T& operator*() const
{
return *m_sp;
}
T& operator*()
{
detach();
return *m_sp;
}
const T* operator->() const
{
return m_sp.operator->();
}
T* operator->()
{
detach();
return m_sp.operator->();
}
};
And I would use it in my multithreaded application on map object, which is shared.
map<unsigned int, LPOBJECT> map;
So I've assigned it to template and now I have :
CowPtr<map<unsigned int, LPOBJECT>> map;
And now my questions :
How I should propertly take instance of the map for random thread which want only read map objects ?
How I should modify map object from random thread, for ex. insert new object or erase it ?
The code you post is poor to the point of being unusable; the
author doesn't seem to understand how const works in C++.
Practically speaking: CoW requires some knowledge of the
operations being done on the class. The CoW wrapper has to
trigger the copy when an operation on the wrapped object might
modify; in cases where the wrapped object can "leak" pointers
or iterators which allow modification, it also has to be able to
memorize this, to require deep copy once anything has been
leaked. The code you posted triggers the copy depending on
whether the pointer is const or not, which isn't at all the same
thing. Thus, with an std::map, calling std::map<>::find on
the map should not trigger copy on write, even if the pointer
is not const, and calling std::map<>::insert should, even if
the pointer is const.
With regards to threading: it is very difficult to make a CoW
class thread safe without grabbing a lock for every operation
which may mutate, because it's very difficult to know when
the actual objects are shared between threads. And it's even
more difficult if the object allows pointers or iterators to
leak, as do the standard library objects.
You don't explain why you want a thread-safe CoW map. What's
the point of the map if each time you add or remove an element,
you end up with a new copy, which isn't visible in other
instances? If it's just to start individual instances with
a copy of some existing map, std::map has a copy constructor
which does the job just fine, and you don't need any fancy
wrapper.
How does this work?
The class class CowPtr does hold a shared pointer to the underlying object. It does have a private method to copy construct a new object and assign the pointer to to the local shared pointer (if any other object does hold a reference to it): void detach().
The relevant part of this code is, that it has each method as
const return_type&
method_name() const
and once without const. The const after a method guarantees that the method does not modify the object, the method is called a const method. As the reference to the underlying object is const too, that method is being called every time you require a reference without modifying it.
If however you chose to modify the Object behind the reference, for example:
CowPtr<std::map<unsigned int, LPOBJECT>> map;
map->clear();
the non-const method T& operator->() is being called, which calls detach(). By doing so, a copy is made if any other CowPtr or shared_ptr is referencing the same underlying object (the instance of <unsigned int, LPOBJECT> in this case)
How to use it?
Just how you would use a std::shared_ptr or boost::shared_ptr. The cool thing about that implementation is that it does everything automatically.
Remarks
This is no COW though, as a copy is made even if you do not write, it is more a Copy if you do not guarantee that you do not write-Implementation.

Does Passing STL Containers Make A Copy?

I can't remember whether passing an STL container makes a copy of the container, or just another alias. If I have a couple containers:
std::unordered_map<int,std::string> _hashStuff;
std::vector<char> _characterStuff;
And I want to pass those variables to a function, can I make the function as so:
void SomeClass::someFunction(std::vector<char> characterStuff);
Or would this make a copy of the unordered_map / vector? I'm thinking I might need to use shared_ptr.
void SomeClass::someFunction(std::shared_ptr<std::vector<char>> characterStuff);
It depends. If you are passing an lvalue in input to your function (in practice, if you are passing something that has a name, to which the address-of operator & can be applied) then the copy constructor of your class will be invoked.
void foo(vector<char> v)
{
...
}
int bar()
{
vector<char> myChars = { 'a', 'b', 'c' };
foo(myChars); // myChars gets COPIED
}
If you are passing an rvalue (roughly, something that doesn't have a name and to which the address-of operator & cannot be applied) and the class has a move constructor, then the object will be moved (which is not, beware, the same as creating an "alias", but rather transferring the guts of the object into a new skeleton, making the previous skeleton useless).
In the invocation of foo() below, the result of make_vector() is an rvalue. Therefore, the object it returns is being moved when given in input to foo() (i.e. vector's move constructor will be invoked):
void foo(vector<char> v);
{
...
}
vector<char> make_vector()
{
...
};
int bar()
{
foo(make_vector()); // myChars gets MOVED
}
Some STL classes have a move constructor but do not have a copy constructor, because they inherently are meant to be non-copiable (for instance, unique_ptr). You won't get a copy of a unique_ptr when you pass it to a function.
Even for those classes that do have a copy constructor, you can still force move semantics by using the std::move function to change your argument from an lvalue into an rvalue, but again that doesn't create an alias, it just transfers the ownership of the object to the function you are invoking. This means that you won't be able to do anything else with the original object other than reassigning to it another value or having it destroyed.
For instance:
void foo(vector<char> v)
{
...
}
vector<char> make_vector()
{
...
};
int bar()
{
vector<char> myChars = { 'a', 'b', 'c' };
foo(move(myChars)); // myChars gets MOVED
cout << myChars.size(); // ERROR! object myChars has been moved
myChars = make_vector(); // OK, you can assign another vector to myChars
}
If you find this whole subject of lvalue and rvalue references and move semantics obscure, that's very understandable. I personally found this tutorial quite helpful:
http://thbecker.net/articles/rvalue_references/section_01.html
You should be able to find some info also on http://www.isocpp.org or on YouTube (look for seminars by Scott Meyers).
Yes, it'll copy the vector because you're passing by value. Passing by value always makes a copy or move (which may be elided under certain conditions, but not in your case). If you want to refer to the same vector inside the function as outside, you can just pass it by reference instead. Change your function to:
void SomeClass::someFunction(std::vector<char>& characterStuff);
The type std::vector<char>& is a reference type, "reference to std::vector<char>". The name characterStuff will act as an alias for the object referred to by _characterStuff.
C++ is based on values: When passing object by value you get independent copies. If you don't want to get a copy, you can use a reference or a const reference, instead:
void SomeClass::someFunction(std::vector<char>& changable) { ... }
void SomeClass::otherFunction(std::vector<char> const& immutable) { ... }
When the called function shouldn't be able to change the argument but you don't want to create a copy of the object, you'd want to pass by const&. Normally, I wouldn't use something like a std::shared_ptr<T> instead. There are uses of this type by certainly not to prevent copying when calling a function.