I recently experimented again with C ++ 11, after some absence, and after reading many articles on the Internet, I am now completely confused by what is the most efficient way to return large objects from factory functions (mainly analyzing data from a database).
I became a fan of unique_ptr, but I read in several articles that because of the new constructors move it is now quite possible to return a large vector by value, and because of this new semantics it should be as fast as copying a single pointer.
To try this, I wrote a small test program with outputs in various constructors:
#include <iostream>
and tested using
C fooVal() { cout << "In fooVal\n"; string str = "value return"; C c(str); return c; } C& fooRef() { cout << "In fooRef\n"; string str = "reference return"; C* pC = new C( str ); return *pC; } C* fooPtr() { cout << "In fooPtr\n"; string str = "classical pointer return"; C* pC = new C( str ); return pC; } unique_ptr<C> fooUPtr() { cout << "In fooUPtr\n"; string str = "unique_ptr return"; return unique_ptr<C>(new C(str)); } shared_ptr<C> fooSPtr() { cout << "In fooSPtr\n"; string str = "shared_ptr return"; return shared_ptr<C>(new C(str)); }
Now, if I just compile it as-is, the compiler optimizes ("elides") the various constructor calls, and I get the output:
In fooVal Constructing a C named 'value return' cv constructed In fooRef Constructing a C named 'reference return' cr constructed In fooPtr Constructing a C named 'classical pointer return' *pC constructed In fooUPtr Constructing a C named 'unique_ptr return' *upC constructed In fooSPtr Constructing a C named 'shared_ptr return' *spC constructed Alive: value return, reference return, classical pointer return, unique_ptr return. Destructing a C named 'shared_ptr return' Destructing a C named 'unique_ptr return' Destructing a C named 'value return'
OK, but you can see how many copies have been optimized. To find out what the βspecifiedβ behavior is, I compiled it using the -fno-elide-constructors flags (I use Apple LLVM version 4.2 (clang-425.0.28)). But then I get the following output:
In fooVal Constructing a C named 'value return' Destructing a C named 'value return' Move-constructing a C named ' [moved]' Destructing a C named '' cv constructed In fooRef Constructing a C named 'reference return' cr constructed In fooPtr Constructing a C named 'classical pointer return' *pC constructed In fooUPtr Constructing a C named 'unique_ptr return' *upC constructed In fooSPtr Constructing a C named 'shared_ptr return' *spC constructed Alive: [moved], reference return, classical pointer return, unique_ptr return. Destructing a C named 'shared_ptr return' Destructing a C named 'unique_ptr return' Destructing a C named ' [moved]'
So, it is clear that something suspicious is happening with the return value of the object. Obviously, this is more than just a small problem, because I expected -fno-elide-constructors to not change the semantics, but only the number of involved constructors.
So I ask:
- What's happening? Why does the value object "lose" its string parameter? And where?
- Returning the value seems to have problems, while others are working fine. So why do people recommend these days that we are returning in value and βthe system takes care of the restβ?
- What is a good way to return large objects?
- Am I making a mistake somewhere that I don't see?
Thanks!