Michael Vlach Michael Vlach - 1 year ago 200
C++ Question

Using C++11 range-based for loop correctly in Qt

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 interator 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 easier/shorter way to achieve the same I would like to hear it.

Answer Source
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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download