for (auto&& elt : range) Still Always Works

Just to drive home the point I made in my previous post for (auto&& elt : range) Always Works” (2018-12-15), here’s an example from elsewhere in the C++ blogosphere. Eric Niebler writes one of his for-loops like this:

// Display the first 10 triples
for (auto triple : triples | view::take(10)) {
    cout << '('
         << get<0>(triple) << ','
         << get<1>(triple) << ','
         << get<2>(triple) << ')' << '\n';
}

(This is by far the most readable snippet of his Pythagorean-triples program. And yes, there’s a using namespace std; in there to make it all stick together.)

Since Eric pretty much wrote Ranges, I thought there might be a subtle reason he went with the un-idiomatic for (auto triple : range) instead of the “Always Works” version for (auto&& triple : range). It would be a tough blow for my guideline if auto&& were somehow incompatible with Ranges!

So I went to Godbolt and tried out the same program with auto and with auto&&. The result? With auto&&, the compiled program is two instructions shorter. (Look around line 138 in the assembly listing for the major difference: we save a copy in the inner loop.)

So,

Use for (auto&& elt : range). It Always Works.

(And also probably “Don’t use Ranges,” at least not on Godbolt Compiler Explorer. If you do, you’ll need to know about the “Clear cache & recompile” button in the lower left corner of the compiler pane. I had to click it about a dozen times before Eric’s toy program managed to sneak in under CE’s eight-second limit on compilation time.)

Posted 2018-12-27