I am new to C++ coming from a Python background. I am currently learning deque and I am getting confused on how to pop the last element and use it.
From cppreference.com it mentions that pop_back would remove the last element of the container and the references to the erased element in invalidated.
Example:
std::deque<int> numbers = {1, 2, 3};
int topNumber = numbers.back(); // Return reference to top element
numbers.pop_back(); // remove the last element and now topNumber reference is invalidates
Question: how is it possible to use the top element after popping it? Does .back() return a copy in the example above, and if so why?
Does .back() return a copy in the example above
No, back() return a reference to the last value in the queue.
int topNumber = numbers.back();
The reference is assigned to an int. This effectively copies the referenced object into a completely different and unrelated object called topNumber.
When a reference to some object gets assigned to something else, that not itself a reference, a copy of the referenced object gets made, that's how C++ works (and if it's assigned to a reference, the copy is made into the assigned-to referenced object).
From that point on, the last value in the deque can be removed. It will be gone. But its copy still remains in topNumber.
Related
I'm using a deque in one of my C++ programs, and was reading the documentation for insert on cppreference.com. Most of it made sense except this bit:
All iterators, including the past-the-end iterator, are invalidated.
References are invalidated too, unless pos == begin() or pos == end(),
in which case they are not invalidated.
What does this mean? Is this saying that references to the deque itself are invalidated, or references to its elements, or references to the iterators? Or something else entirely?
Here's a link the doc in question: http://en.cppreference.com/w/cpp/container/deque/insert
A deque is an object. It is a container, therefore it contains other objects within it's internal storage. Those are elements stored in a deque.
You can access those elements. Accessing an element is basically getting a reference back from the container. If you check it, all of the methods under element access section return a reference type.
You can make a copy of accessed element, but you can store the reference itself. T foo = d.front(); vs T& bar = d.front();. (Let d be some std::deque<T>)
A reference to a deque would be auto& ref_d = &d;. This is something else.
So:
1. What effect does “insert” have on references to deques?
None. references to d are fine.
2. What does this mean?
A deque is designed in such a way that inserting at the beginning or at the end of it does not invalidate the references to the elements which you might have already stored. Though if you insert in the middle, the elements might move in memory. Note that the bar is not touched. Precisely because it cannot be, it gets invalidated. Previously obtained reference (or iterator) doesn't point to anything meaningful anymore, thus dereferencing it is illegal.
3. Is this saying that references to the deque itself are invalidated?
Nope, as in 1.
4. or references to its elements [are invalidated]?
Yes, as in 2.
5. or references to the iterators [are invalidated]?
You again seem to confuse what is what. A reference to an iterator would be std::deque<T>::iterator& iter_ref;, if you obtain an iterator from a deque. E.g. auto iter = d.begin(); and make a reference to it iter_ref = &iter;, an insert doesn't make *iter_ref illegal, it invalidates the iterator, so *iter is illegal (or **ref_iter).
Note: I am not saying that something like std::deque<T>& ref_d or std::deque<T>::iterator& iter_ref make sense, but this is semantical meaning of "reference to a deque" and "reference to an interator".
I explore msvc 2013 STL implementation and found this implemantation of std::vector::push_back:
void push_back(const value_type& _Val)
{ // insert element at end
if (_Inside(_STD addressof(_Val))) // <-- is this check really necessary?
{ // push back an element
size_type _Idx = _STD addressof(_Val) - this->_Myfirst;
if (this->_Mylast == this->_Myend)
_Reserve(1);
this->_Getal().construct(this->_Mylast,
this->_Myfirst[_Idx]);
++this->_Mylast;
}
else
{ // push back a non-element
if (this->_Mylast == this->_Myend)
_Reserve(1);
this->_Getal().construct(this->_Mylast,
_Val);
++this->_Mylast;
}
}
And I have a question: is check
if (_Inside(_STD addressof(_Val)))
really neccessary? This condition checks is _Val belongs to this vector or not. For example this condition is true in cases like:
std::vector<int> v(1);
v.push_back(v[0]);
What the difference between push_back of element of same vector and other values?
I don't know if that extra check is mandated by the standard, but it avoids a subtle bug that can happen if you push into a vector an element of itself.
Suppose you do, as in your example,
std::vector<int> v(1);
v.push_back(v[0]);
in an implementation that doesn't do that check. Now, if the vector's capacity is more than 1, all is fine and good, v[0] is just copy-constructed in the correct position.
But what happens if the vector has to reallocate? In that case, the reference to v[0] that was passed to push_back gets invalidated just after the reallocation, so push_back will try to copy inside the vector an object that no longer exists.
The code in the implementation you posted avoids the problem by checking if the reference points to an element inside the vector, and, in that case, it takes note of its index. After the reallocation, even if the reference is invalidated, the index is still correct, so the copy can be performed without risks.
The code in the two code paths is blatantly different:
construct(this->_Mylast, this->_Myfirst[_Idx])
Compared to:
construct(this->_Mylast, _Val)
The reason is of course that if the vector's capacity is exhausted, it needs to reallocate its storage, which invalidates references.
If the argument, _Val, is not part of the vector, then that has no relevance, but if it is, then we can no longer use it after reallocating. So in the first code path, where _Val is part of the vector, the value is referred to by its vector index rather than by the original function argument.
I have some function
void print_elem(const std::vector<int>::iterator it, const std::vector<int> &vec) {/*.....*/}
If I leave out that the vector is a reference to the original object, I get copies of the vector. Why doesn't the same hold true for the iterator? Why doesn't the iterator need to be a reference also?
For instance if I wanted to iterate through the vector, print each element and wanted to stop when I hit the end of the vector, unless I pass a reference to the vector the iteration just continuously iterates through the first vector copy. But if I pass through a reference the iteration goes through the original vector object. But why does the iterator not get copied the way the vector without reference does?
The iterator models a pointer, and it most likely either is one, or contains one which points to the vector or its contents. When you copy it, the copy is in fact a different iterator, but it stores the same value, so it still points to the same thing, just like a pointer would.
Is it dangerous to returning a pointer out of a std::map::find to the data and using that as opposed to getting a copy of the data?
Currently, i get a pointer to an entry in my map and pass it to another function to display the data. I'm concerned about items moving causing the pointer to become invalid. Is this a legit concern?
Here is my sample function:
MyStruct* StructManagementClass::GetStructPtr(int structId)
{
std::map<int, MyStruct>::iterator foundStruct;
foundStruct= myStructList.find(structId);
if (foundStruct== myStructList.end())
{
MyStruct newStruct;
memset(&newStruct, 0, sizeof(MyStruct));
myStructList.structId= structId;
myStructList.insert(pair<int, MyStruct>(structId, newStruct));
foundStruct= myStructList.find(structId);
}
return (MyStruct*) &foundStruct->second;
}
It would undoubtedly be more typical to return an iterator than a pointer, though it probably makes little difference.
As far as remaining valid goes: a map iterator remains valid until/unless the item it refers to is removed/erased from the map.
When you insert or delete some other node in the map, that can result in the nodes in the map being rearranged. That's done by manipulating the pointers between the nodes though, so it changes what other nodes contain pointers to the node you care about, but does not change the address or content of that particular node, so pointers/iterators to that node remain valid.
As long as you, your code, and your development team understand the lifetime of std::map values ( valid after insert, and invalid after erase, clear, assign, or operator= ), then using an iterator, const_iterator, ::mapped_type*, or ::mapped_type const* are all valid. Also, if the return is always guaranteed to exist, then ::mapped_type&, or ::mapped_type const& are also valid.
As for wise, I'd prefer the const versions over the mutable versions, and I'd prefer references over pointers over iterators.
Returning an iterator vs. a pointer is bad:
it exposes an implementation detail.
it is awkward to use, as the caller has to know to dereference the iterator, that the result is an std::pair, and that one must then call .second to get the actual value.
.first is the key that the user may not care about.
determining if an iterator is invalid requires knowledge of ::end(), which is not obviously available to the caller.
It's not dangerous - the pointer remains valid just as long as an iterator or a reference does.
However, in your particular case, I would argue that it is not the right thing anyway. Your function unconditionally returns a result. It never returns null. So why not return a reference?
Also, some comments on your code.
std::map<int, MyStruct>::iterator foundStruct;
foundStruct = myStructList.find(structId);
Why not combine declaration and assignment into initialization? Then, if you have C++11 support, you can just write
auto foundStruct = myStructList.find(structId);
Then:
myStructList.insert(pair<int, MyStruct>(structId, newStruct));
foundStruct = myStructList.find(structId);
You can simplify the insertion using make_pair. You can also avoid the redundant lookup, because insert returns an iterator to the newly inserted element (as the first element of a pair).
foundStruct = myStructList.insert(make_pair(structId, newStruct)).first;
Finally:
return (MyStruct*) &foundStruct->second;
Don't ever use C-style casts. It might not do what you expect. Also, don't use casts at all when they're not necessary. &foundStruct->second already has type MyStruct*, so why insert a cast? The only thing it does is hide a place that you need to change if you ever, say, change the value type of your map.
Yes,
If you build a generic function without knowing the use of it, it can be dangerous to return the pointer (or the iterator) since it can become un-valid.
I would advice do one of two:
1. work with std::shared_ptr and return that. (see below)
2. return the struct by value (can be slower)
//change the difination of the list to
std::map<int, std::shared_ptr<MyStruct>>myStructList;
std::shared_ptr<MyStruct> StructManagementClass::GetStructPtr(int structId)
{
std::map<int, std::shared_ptr<MyStruct>>::iterator foundStruct;
foundStruct = myStructList.find(structId);
if (foundStruct == myStructList.end())
{
MyStruct newStruct;
memset(&newStruct, 0, sizeof(MyStruct));
myStructList.structId= structId;
myStructList.insert(pair<int, shared_ptr<MyStruct>>(structId, shared_ptr<MyStruct>(newStruct)));
foundStruct= myStructList.find(structId);
}
return foundStruct->second;
std::list<Value> stdList;
stdList.push_back(Value());
Value * ptr = &stdList.back(); // <-- what will this address point to?
If I take the reference returned by back() and implicitly convert it to the less generic Value *, will it point to the last value of the list, or will it point to someplace unexpected?
And is there a way to create an iterator from a pointer, for use with std::list functions such as erase()? I realize generic to specific (iterator to pointer) is far more feasible than going the other direction, but I thought I'd ask anyway.
The pointer will point to the value as it is stored inside the container. The reference did the same thing.
You can't turn that pointer into a list iterator directly, because you've lost all the information about the surrounding structure. To do this you would have to get clever with list::find.
What you are trying to do it is sometimes done using vector. The reason you can turn a vector data element pointer into an iterator (array index) is because you know the structure of a vector.
Please note that list::back() does not return an iterator. It returns a reference. The two are quite different. Are you thinking about list::end()? Or are you genuinely confused between iterators and references? Because you can get a reference from a pointer. You do it like this:
Value& refval = *ptr;
Yes, The pointer point to the last value stored in the list.
We can not create an iterator from a pointer, iterator is a concept for the container (list here), and iterator don't care about what kind of value stored on the list.
Reference and pointer are handle for the value stored on the list, they are interchangeable, we can convert a reference to a pointer and vice versa.