Auto for Types, but Not for Concepts
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
- auto to stick and Changing Your Style
- The Rule of Zero in C++
- Why You Should Separate Commands from Queries
- The C++ Metaclasses Proposal in Less Than 5 Minutes
Share this post!