This question already has answers here:
How can I create objects while adding them into a vector?
(5 answers)
Closed 9 years ago.
This is a pretty straight forward question.
Is there a way to have a vector and initialize an element without constructing and then copying it?
class BigType
{
// has a costly copy constructor
};
int main(void)
{
using std::vector;
vector<BigType> bigTypeVec;
bigTypeVec.push_back(BigType(/*constructor from parameters*/));
// This constructs a temp object, and then copies it to the new element.
}
Of course there are all sorts of work-a-rounds involving vectors of pointers, or instead of using a constructor, initialize an element's components with set functions, however I was wondering if there were a way to do it so that it can call the constructor on the element it allocates during push_back.
Edit: This question was marked as a duplicate, however I had viewed that page and the answers to his question hadn't answered mine. I want to know how to set the value of the element by constructing it once, rather then copy constructing a temporary object into the element. Emplace was a good way to do this.
You could use std::vector<...>::emplace() or, if you want to append the object, std::vector<...>::emplace_back(), to construct the object in place. If you have to use C++03 this member function isn't available. As an approximation you could push_back() and empty object and then swap() your BigType into the corresponding location, assuming you could construct a small empty object.
Note that std::vector<...> isn't necessarily the best data structure if you have huge objects: if the reserved spaces runs out, the vector need to shuffle the objects around to make new space. You might want to use std::deque<...> instead, as it won't leave its objects put (unless you insert into the middle) while having similar access characteristics as std::vector<...>.
with C++11, yes.
bigTypeVec.emplace_back(BigType());
Here's some more info:
http://en.cppreference.com/w/cpp/container/vector/emplace_back
"Is there a way to have a vector and initialize an element without constructing and then copying it?"
Yes.
Consider placement new as a mechanism to side-step the use of a copy assignment and it's use of a temporary.
"Is there a way to have a vector..."
A vector can be built with elements created with the default constructor.
I believe it is possible to define BigType so that no element initialization is required during the bigTypeVec construction. In this way, declaring the vector (or even a simple array) will trigger no element constructor work. Consider these:
vector<BigType> bigTypeVec;
or
BigType bigTypeVec[MAX_BigTypeVecSize];
Note that the array requires BigType to provide a default constructor (or you to provide a big bunch of curly brace array initialzation).
However, I can imagine that you might find value for each BigType element to have an indication that it is or is not initialized.
" and initialize an element without constructing [a temp] and then copying it [, the temp, to the vector]?"
Placement new can then be used to construct the object in place. By passing the address of the desired bigTypeVec element you wish to initialize to the placement new, all the element constructor work will occur where (in memory) you want it. Consider something like:
vector<BigType> bigTypeVec;
BigType* pBT = 0;
pBT = new (&bigTypeVec[0] BigType(<param>); // placement new
pBT = new (&bigTypeVec[1] BigType(<param>);
...
pBT = new (&bigTypeVec[n] BigType(<param>);
Note the discard of pBT. Pointer is not used.
*"I was wondering if there were a way to do it so that it can call the constructor on the element it allocates during push_back."*
At this point, all that remains is to create the simplest class possible that inherits from std::vector() and re-impliments "push back", or perhaps a new method that supports your needs. These methods would seek the vector element that push-back would have found, and use the placement new similar to above.
Are you sure about this?
"This constructs a temp object, and then copies it to the new element."
As far as I know, it will directly create an object at the memory location. Return value optimization. Temporary will not be created.
Am I wrong?
Related
In Item 41 from Effective Modern C++ Scott Meyers mentions this difference and its impact on the efficiency of emplacement with respect to insertion.
I have some doubts about that, but before asking questions about that I need to understand what the difference between these two ways of adding an element is.
Consider the code sample from the book:
std::vector<std::string> vs;
// adding some elements to vs
vs.emplace(v.begin(), "xyzzy");
It's clear that after // adding some elements to vs, it could be that
vs.capacity() == vs.size(), or
vs.capacity() > vs.size().
Correspondingly,
vs has to reallocate, and all pre-existing elments in vs (those named vs[0], vs[1], ... before the reallocation takes place) have to be move-constructed to the new memory locations (the new locations of vs[1], vs[2], ...)
vs has to vs.resize(vs.size() + 1) and all pre-existing elements have to be move-assigned to the next index, obviously processing backward, from the last to the first element.
(Obviously I referred to move operations because std::string offers noexcept move operations. If this is not the case, then the scenario above is slightly different and copy operations will be used instead.)
However, going to the crux of my question, right after the code above, the book reads (my italic)
[…] few implementations will construct the added std::string into the memory occupied by vs[0]. Instead, they'll move-assign the value into place. […]
What are the two scenarios, after the room has been done to accomodate the new element?
If emplace adds the element via move assignment, it means it does v[0] = std::string{strarg}; where strarg == "xyzzy", right?
But what is the other case of contructing the element occupied by v[0]? Is it a placement new? And how would it look like? I guess it should be similar to the chunk of code at the section Placement new here, but I'm not sure how it would look like in this case.
There are many different ways to implement emplace, and the standard is pretty lax on how implementations must do it.
Given a pointer to somewhere allocated by std::allocator_traits<allocator_type>::allocate, the only way for a vector to construct a new object is with std::allocator_traits<allocator_type>::construct. For the default allocator, this will call placement new.
Now if a reallocation did occur, the obvious way to emplace the new element is to call allocator_traits::construct(get_allocator(), ptr, std::forward<Args>(args...)). This will be the equivalent of new (ptr) std::string("xyzzy"). But note that all other elements were also move constructed to the new buffer via allocator_traits::construct(get_allocator(), ptr, std::move(old_ptr)).
If a reallocation didn't occur, most implementations will just construct an element with value_type(std::forward<Args>(args...)) and move-assign from that (equivalent to v[0] = std::string("xyzzy")). This is what libstdc++ does.
Alternatively, instead of move constructing v[0] = std::string("xyzzy"), the object can be destroyed via allocator_traits::destroy ((&v[0])->~value_type() for the default allocator), and then it can be constructed in place via allocator_traits::construct. This seems like it would be harder to implement since special care would need to be taken to ensure that the element isn't destroyed twice if the move constructor throws, which is probably why only "few implementations" will do it.
As an aside, there is no strong exception guarantee, so move_if_noexcept doesn't have to be used and the move constructor may always be called, even if the move constructor is not noexcept.
The dynamically created array of objects need to use a non-default constructor, and the problem I'm running into I think is the syntax. In my mind, the fact that I'm able to do this
int * somePtr = new int[5];
means that I should be able to do this
IntegerSet* someSet = new IntegerSet(this->getLength())[5];
where IntegerSet is a class I have made that represents an integer set. this code is happening inside one of IntegerSets member function. When I try this I get a syntax error
"cannot convert from IntegerSet to IntegerSet*"
I understand what this means, the two types aren't equivalent, but I can't see the difference between doing what I did in part 1 and part 2, besides the fact that part 2 has to have an argument list passed as the constructor. So it is in that part of the code that I suspect I have the syntax wrong
new expression allows only default initialization, you can not do this within single new expression. What you could do is allocate raw memory and construct objects one by one using placement new (see this answer in Object array initialization without default constructor)
Or yet even better, don't use C-style arrays. Instead, use some STL container such as std::vector and it's constructor, where the 2nd argument is a value that will be used to initialize elements:
std::vector<IntegerSet> integers(5, IntegerSet(this->getLength()) );
There is an easy way out of this problem: add a default constructor to IntegerSet that does not acquire memory and any other resources. This way you can allocate an array of IntegerSet with new and then fill each element of the array on the next step.
Even better solution: use std::vector<IntegerSet> and emplace_back() to initialize each element of the array using a non-default constructor.
The question is simple and short, but I did not manage to find a good solution yet: How should I insert objects into a std::vector?
Clearly, I can do vec.push_back(MyObject(param1, param2, param3)). But is this a reasonable solution, considering the call-by-value and therefore copying of MyObject? (In case the object is large or not copyable.)
I also wonder whether it is save to return a pointer on the newly constructed and inserted object.
With C++11 you can construct objects in place with vector::emplace_back().
The dynamically created array of objects need to use a non-default constructor, and the problem I'm running into I think is the syntax. In my mind, the fact that I'm able to do this
int * somePtr = new int[5];
means that I should be able to do this
IntegerSet* someSet = new IntegerSet(this->getLength())[5];
where IntegerSet is a class I have made that represents an integer set. this code is happening inside one of IntegerSets member function. When I try this I get a syntax error
"cannot convert from IntegerSet to IntegerSet*"
I understand what this means, the two types aren't equivalent, but I can't see the difference between doing what I did in part 1 and part 2, besides the fact that part 2 has to have an argument list passed as the constructor. So it is in that part of the code that I suspect I have the syntax wrong
new expression allows only default initialization, you can not do this within single new expression. What you could do is allocate raw memory and construct objects one by one using placement new (see this answer in Object array initialization without default constructor)
Or yet even better, don't use C-style arrays. Instead, use some STL container such as std::vector and it's constructor, where the 2nd argument is a value that will be used to initialize elements:
std::vector<IntegerSet> integers(5, IntegerSet(this->getLength()) );
There is an easy way out of this problem: add a default constructor to IntegerSet that does not acquire memory and any other resources. This way you can allocate an array of IntegerSet with new and then fill each element of the array on the next step.
Even better solution: use std::vector<IntegerSet> and emplace_back() to initialize each element of the array using a non-default constructor.
This is presumable a simple C++ question, but I'm relearning C++ and don't know some of the basics. I have a class that includes a struct with a vector of objects in it, so something like this:
struct my_struct{
Irrelevant_Object object,
vector<tuple> tuple_list;
}
The struct and the tuple (another struct) are predefined by the architecture and given to me in my method; so I can't change them. I want to generate and insert a tuple into the originaly empty tuple_list.
The simple solution is have a method which allocates a new tuple object, fills in the tuple data, then call tuple_list.push_back() and pass in the allocated tuple. But this would require allocating a new tuple only to have the push_back method copy all of the contents of the (large) tuple struct into an already defined memory space of the vector. So I'm paying the expense of an allocation/delete as well as the lesser expense of copying the tuple contents into the vector to do it this way. It seems rather inefficent, and since this method would be in the critical path of the function I would prefer something faster (admitedly I doubt this method would be the bottle-neck, and I know early optimization == bad. However, I'm asking this question more to learn something about C++ syntax then out of a deperate need to actually do this in my code).
So my question is, is there a quicker way to fill the contents of my tuple list without allocating and copying a tuple? If this was an array I could make the array as large as I want, then past a reference to tuple_list[0] to the function that creates the tuple. That way the funciton could fill the empty contents of the already allocated tuple within the array without allocating a new one or copying from one tuple to another. I tried to do that with the vector out of curiousity and ended up with a seg fault when my itterator pointed to 0x0, so I assume that syntax doesn't work for vectors. So is there a quick way of doing this assignment?
Since this is a question as much to learn the language as for actual use feel free to throw in any other tangentally relevant stuff you think are interesting, I'm looking to learn.
Thanks.
In C++11, you can use std::vector::emplace_back, which constructs the new object in-place, therefore there is no copying when you use this method.
By using this method, you could do this:
my_struct some_struct;
some_struct.tuple_list.emplace_back(1, 5, "bleh");
Assuming your tuple object contains this constructor:
tuple::tuple(int, int, const std::string&)
Edit: You can also use move semantics to store a pre-allocated tuple:
my_struct some_struct;
tuple a_tuple;
/* modify a_tuple, initialize it, whatever... */
some_struct.push_back(std::move(a_tuple)); // move it into your vector
Or use a reference to the tuple after it has been stored in the vector:
my_struct some_struct;
some_struct.tuple_list.emplace_back(1, 5, "bleh");
// store a reference to the last element(the one we've just inserted)
tuple &some_tuple = some_struct.tuple_list.back();
some_tuple.foo();
On all of the above solutions you're creating only one tuple while also avoiding copying.