Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to initialise a STL vector/list with a class without invoking the copy constructor

Tags:

I have a C++ program that uses a std::list containing instances of a class. If I call e.g. myList.push_back(MyClass(variable)); it goes through the process of creating a temporary variable, and then immediately copies it to the vector, and afterwards deletes the temporary variable. This is not nearly as efficient as I want, and sucks when you need a deep copy.

I would love to have the constructor of my class new something and not have to implement a copy constructor just to allocate my memory for the second time and waste runtime. I'd also rather not have to immediately find the class instance from the vector/list and then manually allocate the memory (or do something horrible like allocate the memory in the copy constructor itself).

Is there any way around this (I'm not using Visual Studio BTW)?

like image 482
Warpspace Avatar asked Apr 05 '10 18:04

Warpspace


People also ask

How do you initialize a vector of an object?

How to Initialize a Vector in C++ Using the push_back() Method. push_back() is one out of the many methods you can use to interact with vectors in C++. It takes in the new item to be passed in as a parameter. This allows us to push new items to the last index of a vector .

Does Emplace_back call constructor?

So you can emplace_back does use the desired constructor to create the element and call copy constructor when it need to grow the storage. You can call reserve with enough capacity upfront to avoid the need to call copy constructor.


2 Answers

C++0x move constructors are a partial workaround: instead of the copy constructor being invoked, the move constructor would be. The move constructor is like the copy constructor except it's allowed to invalidate the source argument.

C++0x adds another feature which would do exactly what you want: emplace_back. (N3092 §23.2.3) You pass it the arguments to the constructor, then it calls the constructor with those arguments (by ... and forwarding) so no other constructor can ever be invoked.

As for C++03, your only option is to add an uninitialized state to your class. Perform actual construction in another function called immediately after push_back. boost::optional might help you avoid initializing members of the class, but it in turn requires they be copy-constructible. Or, as Fred says, accomplish the same thing with initially-empty smart pointers.

like image 124
Potatoswatter Avatar answered Oct 18 '22 19:10

Potatoswatter


Ahem. In the interests of science, I've whipped up a tiny test program to check whether the compiler elides the copy or not:

#include <iostream> #include <list> using namespace std;  class Test { public:   Test() { cout<<"Construct\n"; }   Test(const Test& other) { cout<<"Copy\n"; }   Test& operator=(const Test& other) { cout<<"Assign\n"; return (*this); } };  Test rvo() { return Test(); } int main() {   cout<<"Testing rvo:\n";   Test t = rvo();   cout<<"Testing list insert:\n";   list<Test> l;   l.push_back(Test()); } 

And here's my output on MSVC++2008:

 Testing rvo: Construct  Testing list insert: Construct Copy 

It's the same for both debug and release builds: RVO works, temporary object passing isn't optimized.
If I'm not mistaken, the Rvalue references being added in the C++0x standard are intended to solve this very problem.

like image 24
tzaman Avatar answered Oct 18 '22 17:10

tzaman