Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use C++11 range-based for with QJsonArray

Tags:

for-loop

c++11

qt

With old-style loops, I can delve into a QJsonArray and, in the example below, add element "foo" with contents of existing element "bar" for each array item. How can I do this using C++11 range-based for?

// QJsonArray oldArray contains an array of which one element is "bar"
QJsonArray newArray;
int i, b = oldArray.count();
for (i=0; i<n; ++i) {
    QJsonObject element = oldArray.at(i).toObject();
    element["foo"] = element["bar"];
    newArray.append(element);
}

I have tried the following (admittedly as trial and error):

auto&

for (auto& element : oldArray) {
    element["foo"] = element["bar];
    newArray.append(element);
}

I get the error

non-const lvalue reference to type 'QJsonValueRef' cannot bind to a temporary of type 'QJsonValueRef'

const auto&

for (const auto& element : oldArray) {
...

I get a warning

loop variable 'element' is always a copy because the range of type 'QJsonArray' does not return a reference

const auto

for (const auto element : oldArray) {
    element["foo"] = element["bar];
    ...

I get the error

no viable overloaded operator[] for type 'const QJsonValueRef'

relating to element["bar"]

like image 820
Paul Masri-Stone Avatar asked Apr 23 '18 10:04

Paul Masri-Stone


1 Answers

The problem is that the iterator for QJsonArray returns a temporary QJsonValueRef object, and lvalue references can not bind to temporaries. We can either hold that temporary by value:

// QJsonArray oldArray contains an array of which one element is "bar"
QJsonArray newArray;
for (auto v : oldArray) {
    QJsonObject element = v.toObject();
    element["foo"] = element["bar"];
    newArray.append(element);
}

In this case v is a QJsonValueRef object (similar to what oldArray.at(i) gives in the old-style loop). After that, we convert that QJsonValueRef to a QJsonObject using .toObject().

Or we can use forwarding references since they can bind to rvalues:

for (auto&& v : oldArray) {
    ...
}

In this case v is deduced to be an rvalue reference to a QJsonValueRef.

Both solutions are identical in terms of the number of objects being created/destructed (since in the former, the copy is elided under C++17 guaranteed copy elision rules or even in pre-C++17 when using any decent compiler. In the latter, the reference is bound to the temporary and this prolongs its lifetime to match the whole iteration).


Notes

  1. This problem is similar to what happens when using range-based-for with a std::vector<bool>; since both std::vector<bool> and QJsonArrayhave iterators that return proxy objects.
  2. If using the Clang code model, the second solution generates the warning "loop variable 'v' is always a copy because the range of type 'QJsonArray' does not return a reference". See this question for an explanation.
like image 153
Paul Masri-Stone Avatar answered Oct 24 '22 21:10

Paul Masri-Stone