Jonathan Boccara's blog

Auto for Types, but Not for Concepts

Published December 4, 2020 - 0 Comments

AAA.

Three letters that the C++ community associates to the early times of Modern C++.

AAA. Almost Always Auto.

Is this still valid today, now that C++20 is the latest standard?

Exploring the reasons behind the AAA guideline allow to better understand auto and what it can express in our code. Especially since the guideline has evolved with the new versions of C++. Putting AAA in this perspective can give us a new light on other features of Modern C++.

Thanks to Herb Sutter for reviewing this article.

Almost, Always and Auto

AAA was a popular acronym back when C++11 and C++14 came out. It was Herb Sutter who presented it first as I reckon, for example in his CppCon 2014 talk Back to the Basics! Essentials of Modern C++ Style.

Auto

We’ve seen that auto has several roles. auto to track:

auto myObject = f();

And the less known auto to stick:

auto myObject = MyType{value};

Always

In previous articles we’ve been through the various reasons behind the “Always” in the AAA guideline, that is to say the advantages of using auto to stick and auto to track.

Almost?

The first A, the “Almost” of the AAA, was here because auto to stick would not play well with types that were expensive to move or couldn’t be moved at all.

For example, since std::mutex and std::lock_guard are not moveable, the following code using auto to stick doesn’t compile in C++14:

auto m = std::mutex{}; // missing move constructor
auto lock = std::lock_guard<std::mutex>{m};  // missing move constructor
ForwardIterator p = algo(x, y, z);

For moveable types the compiler is allowed to invoke a move constructor, but for types that are not cheap to move, for example std::array, that can be a waste of resources:

auto myArray = std::array<int, 10>{}; // the whole array may be copied

Note however that compilers are allowed to optimize this away and not call the move constructor.

But C++17 introduces a new feature to the language: guaranteed copy elision. This means that the compiler have to treat the following expression:

auto myObject = MyType{value};

the same way as it would treat that one:

MyType myObject{value};

This means that the reason behind the “Almost” of AAA is gone in C++17.

In C++17, AAA becomes AA: Always Auto, also defended by Herb Sutter.

auto and concepts

I was thinking it was all well and clear, until I read the ES.11 guideline of the CppCoreGuidelines.

By the way, if you haven’t heard of the CppCoreGuidelines yet, you should start having a look at them. They’re a huge collection of guidelines on how to use C++ well to produce expressive and correct code. They’re a really good read. As a complementary read, Rainer Grimm has spent months (if not years) writing blog posts about those CppCoreGuidelines on Modernes C++.

One of those guidelines, the ES.11 guideline, talks about using auto. Essentially, it’s about encouraging C++ developers to use auto to track, by outlining its multiple benefits.

But right at the end of the guideline, there is this troubling sentence:

Note
When concepts become available, we can (and should) be more specific about the type we are deducing:

ForwardIterator p = algo(x, y, z);

Can you see the apparent contradiction?

On the one hand, the guideline exhorts the usage of auto to avoid writing out information the compiler already knows.

But on the other hand, the guideline tells us to write out the concept ourselves when there is one.

This seems to go directly against Herb’s AA guideline. Why is there such a difference between types and concepts?

I’ve reported this question to Herb Sutter, who happens to be one of the authors of the Cpp Core Guidelines too.

Herb kindly answered by explaining that when we use the ForwardIterator concept in ForwardIterator p = algo(x,y,z), it is equivalent to using auto to deduce the type, but with an additional step of constraining the type with a concept.

In other terms, it is equivalent to this:

auto p = algo(x, y, z); // the usual AA
static_assert(ForwardIterator<decltype(p)>); // additional constraint on the type

An interesting note is that the Range TS had that type of code, as it was written before concepts became standard in C++.

This allows to clarify our intentions, both to the compiler and to the other developers that read the code.

Interestingly, as Herb Sutter noted, the auto hasn’t completely disappeared from the syntax of concepts in C++20:

template<typename T>
concept C = true;

int main() {
    C auto i = 1;
}

Exploring the evolution of the AAA guideline allowed us to better understand guaranteed copy elision, atypically moveable types, a Cpp Core Guidelines, and C++ concepts, and how to express our intentions in code.

What do you think of the new AA guideline? Do you follow it in your code?

And it may be early to tell, but do you think your code will benefit from constraining auto-deduced types with concepts?

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin