According to this talk there is a certain pitfall when using C++11 range base for
on Qt containers. Consider:
QList<MyStruct> list; for(const MyStruct &item : list) { //... }
The pitfall, according to the talk, comes from the implicit sharing. Under the hood the ranged-based for
gets the iterator from the container. But because the container is not const
the iterator will be non-const
and that is apparently enough for the container to detach.
When you control the lifetime of a container this is easy to fix, one just passes the const
reference to the container to force it to use const_iterator
and not to detach.
QList<MyStruct> list; const Qlist<MyStruct> &constList = list; for(const MyStruct &item : constList) { //... }
However what about for
example containers as return values.
QList<MyStruct> foo() { //... } void main() { for(const MyStruct &item : foo()) { } }
What does happen here? Is the container still copied? Intuitively I would say it is so to avoid that this might need to be done?
QList<MyStruct> foo() { //... } main() { for(const MyStruct &item : const_cast<const QList<MyStruct>>(foo())) { } }
I am not sure. I know it is a bit more verbose but I need this because I use ranged based for loops heavily on huge containers a lot so the talk kind of struck the right string with me.
So far I use a helper function to convert the container to the const
reference but if there is a shorter/easier way to achieve the same I would like to hear it.
Range-based for loop in C++ It executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.
To iterate over a list, you can either use index positions or QList's Java-style and STL-style iterator types: Indexing: for (int i = 0; i < fonts. size(); ++i) cout << fonts.at(i).
C++11 introduced the ranged for loop. This for loop is specifically used with collections such as arrays and vectors. Here, the ranged for loop iterates the array num from beginning to end. The int variable var stores the value of the array element in each iteration.
template<class T> std::remove_reference_t<T> const& as_const(T&&t){return t;}
might help. An implicitly shared object returned an rvalue can implicitly detect write-shraring (and detatch) due to non-const iteration.
This gives you:
for(auto&&item : as_const(foo())) { }
which lets you iterate in a const way (and pretty clearly).
If you need reference lifetime extension to work, have 2 overloads:
template<class T> T const as_const(T&&t){return std::forward<T>(t);} template<class T> T const& as_const(T&t){return t;}
But iterating over const rvalues and caring about it is often a design error: they are throw away copies, why does it matter if you edit them? And if you behave very differently based off const qualification, that will bite you elsewhere.
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