C++ Logo

sg20

Advanced search

Re: [SG20] Difficulties in teaching the use of C++20 concepts

From: Jon Kalb <jon_at_[hidden]>
Date: Tue, 17 Dec 2019 20:53:34 -0500
> On Dec 15, 2019, at 11:01 AM, Tony V E via SG20 <sg20_at_[hidden]> wrote:
>
> On Sun, Dec 15, 2019 at 8:56 AM Martin Beeger via SG20 <sg20_at_[hidden] <mailto:sg20_at_[hidden]>> wrote:
> Hallo Herb!
>
> Am 15.12.19 um 06:45 schrieb Herb Sutter:
>
>>
>>
>> 2. I don't teach to always deduce a type. There are two forms I've always taught:
>>
>>
>>
>> auto x = expr; // to make the type track, deduce it
>>
>>
>>
>> auto x = type{expr}; // to make the type stick, commit to it
>>
>>
>>
>> both of which are still auto. So although the first form is great for making code less brittle under maintenance including to avoid introducing silent conversions/temporaries as types change, the second form is still always available (and actively recommended) when it's appropriate to commit to a particular type, and "AAA" has always taught both of these styles.
>>
>>
>>
>> However, note that the advice to use concepts constraints applies only to the first form, which is deduced. It’s not very useful in the second form, which already commits to a concrete type.
>>
> Immediately deduced types, as in the second example have a much stronger commitment so to speak, they are not only bound to a concept, but even to a type. I totally agree that these should be treated as a different form of auto.
>
>
>>
>> 3. The "Almost" in "Almost Always Auto" is now largely gone thanks to C++17 guaranteed copy elision -- so these days the idiom should be renamed pretty much "Always Auto," though of course people are as always free to disagree.
>>
>>
>>
>>
> I favor dying on the overconstrained hill today, as I can easily break and fix my code to be less constrained on demand, as dying on the underconstrained hilll (semantically broken code compiles) hill, as this is hard to find out for me. So I get you, but I will not walk this path in the meantime.
>
> But to sum up regarding the use of auto, a post-C++20 guideline could be (a hopefully much more well-formulated and littered with examples version of the following):
>
> 1. whenever the type of the expression is mentioned on the right hand of a statement, just put auto on the left side for brevity instead of repeating yourself on the left hand.
>
> 2. whenever auto represents a generic value whose type is not known locally, look for a fitting named constrained and apply it for the auto parameter to ensure semantic correctness of the using code without loss of genericity.
>
> 3. whenever you develop code and you are not thinking of its correctness in terms of concrete types but in form of general concepts, use auto.
>
> 4. whenever you write functions, try to think of the problem in you solve as a abstract problem and about generic solutions, not only about one for your concrete types. Look for an exisisting solution to a general problem.
>
> As you apply the last rule to your coding (in line with Sean Parents Better Code principles) you will either find an a solution already there, if you don't, what you will write will now fall under rule 3, which will in turn then fall under rule 2.
>
> Is that an accurate representation of what the your AAA (now AA) rule intends to achieve and how you would advise in a C++20 world?
>
> I as and AAA skeptic can totally get behind this new version.
>
> Martin
>
>
>
> I am/was a AAA skeptic as well, and have been waiting for Concept x = ... (or now Concept auto x = ...) as the sweet-spot solution.
>
> Now that we have:
>
> auto x = expr;
> Concept auto x = expr;
>
> doesn't
> int x = expr;
>
> fit the "pattern" better than
>
> auto x = int{expr};
>

I think not. If I see this code:

 int x = expr;

I’m not certain which of these is the author’s intent:

No matter what expr evaluates to, I want x to be an int.
This is legacy code from before auto existed.
I’m guessing that expr returns an int and I’m not using auto out of programming style momentum.

But if I see this:

 auto x{int{expr}};

I’m pretty certain that the meaning is, "No matter what expr evaluates to, I want x to be an int."


> ie
>
> auto x = expr; // whatever expr says
> Number x = expr; //what I generally expect
> int x = expr; // what I precisely expect
>
> The above looks like a teachable pattern to me.
>
> What is the value of
>
> auto x = int{expr};
>
> besides following a pattern of auto on the left?
>

This better conveys the intention that you really want x to be int without regard to the type of expr. The alternative “int x = expr;” just invites someone to notice that expr (either now or the future) might not be an int, and thus reflector it as “auto x{expr};

> It does help match cases of
>
> auto x = make_shared<Foo>(); // type is mentioned on the right.
>
> Is that the main benefit? Sorry if I have missed/forgotten the benefits of the second usage of auto.
>
> Importantly, note this bit from Herb above (where first form is auto x = expr): "the first form is great for making code less brittle under maintenance including to avoid introducing silent conversions as types change"
>
> Yet second form (auto x = int{expr}) has a slight brittleness under maintenance, including introducing unexpected conversions. Note
>
> auto x = Foo{expr}; // calls explicit constructors
> Foo x = expr; // only calls implicit constructors
>
> If we always use the first form, we lessen the power of implicit vs explicit constructors (they are still useful for function params however, ie func(expr) where expr attempts to be converted to Foo)
>
> Now, we can say auto x = Foo{expr} is "obviously" explicit, since the type was explicitly written, but it is _precisely_ under maintenance where the type of expr changes, and you are now calling a different constructor, one that you weren't expecting, one that was protected with 'explicit'.
>
> Two real-life examples, from Howard Hinnant, paraphrased:
>
> auto ns = nanoseconds{duration}; // duration was a chrono type, but became int, is this still what you wanted???
> vs
> nanoseconds ns = duration; // if duration is strong chrono type, does the conversion you wanted/expected, if duration is int, fails to compile
>
> And
>
> auto sp = shared_ptr<Foo>(ptr); // ptr is a weak_ptr, need shared
> vs
> shared_ptr<Foo> sp = ptr;
>
> When, under maintenance, ptr changes from weak_ptr to raw T* ptr, one of those lines is probably a bug, the other doesn't compile.
> (And yes, maybe they should have called ptr.lock() on the weak ptr, but some people think that was a poorly named function (confusing with mutex etc) and prefer the conversion style.)
>
>
> So I still prefer int x = expr; over auto x = int{expr};, and I think int x = expr nicely follows a pattern with auto x = expr and Concept auto x = expr.
>
> --
> Be seeing you,
> Tony
> --
> SG20 mailing list
> SG20_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/sg20


Received on 2019-12-17 19:56:02