In Qt 5.2.1, how is it that the following code results are different?
QVector<int> c;
if (c.cbegin() != c.begin())
{
std::cout << "Argh!" << std::endl;
}
this prints "argh" but the following doesn't.
QVector<int> c;
if (c.begin() != c.cbegin())
{
std::cout << "Argh!" << std::endl;
}
Notice that cbegin and begin places are switched. But if you change the container state I mean for example push_back something in it, it works correctly. Seems to me that before calling any mutable method on container, cbegin and cend are invalid. Is it a bug or feature?
The behavior you're observing has to do with the order of the calls being made to QVector::begin
and QVector::cbegin
. If the call to the former happens before the call to the latter, then their return values compare equal, but if you reverse the order, they do not compare equal. This can be demonstrated by the following code:
QVector<int> c;
std::cout << static_cast<void const *>(c.begin()) << std::endl;
std::cout << static_cast<void const *>(c.cbegin()) << std::endl;
The addresses printed will be the same. However, if you swap the order of the two print statements, the addresses will be different.
If you dig into the source code for the two member functions, you'll see
inline iterator begin() { detach(); return d->begin(); }
inline const_iterator cbegin() const { return d->constBegin(); }
And tracing back further, the bodies of both d->begin()
and d->constBegin()
are identical. So the difference is in the call to QVector::detach()
in the first case. This is defined as
template <typename T>
void QVector<T>::detach()
{
if (!isDetached()) {
#if QT_SUPPORTS(UNSHARABLE_CONTAINERS)
if (!d->alloc)
d = Data::unsharableEmpty();
else
#endif
reallocData(d->size, int(d->alloc));
}
Q_ASSERT(isDetached());
}
An explanation of what's going on can be found here.
Qt’s containers are implicitly shared – when you copy an object, only a pointer to the data is copied. When the object is modified, it first creates a deep copy of the data so that it does not affect the other objects. The process of creating a deep copy of the day is called detach
Since, STL-style iterators are conceptually just pointers, they make modification to the underlying data directly. As a consequence, non-const iterators detach when created usingContainer::begin()
so that other implicitly shared instances do not get affected by the changes.
So, if the call to QVector::begin()
happens first, then the container's underlying storage is reallocated, and the subsequent call to QVector::cbegin()
will return the same pointer. However, reverse them and the call to QVector::cbegin()
returns a pointer to the shared storage before any reallocation happens.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With