On Dec 18, 2019, at 12:09 AM, Tony V E <tvaneerd@gmail.com> wrote:

On Tue, Dec 17, 2019 at 8:53 PM Jon Kalb <jon@kalbweb.com> wrote:

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;

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.
That may be true, but does it change its intent?  "This is legacy code..." is not an option for "was this the author's intent". Is there 3 choices in this list or 2?

Yes there is different intent here. The first means that thought has gone into it and x should be an int. The could should be changed to: auto x{int{expr}};
The second means that no thought has been put into it and it isn’t clear if it should be auto x{expr}; or auto x{int{expr}};
  • I’m guessing that expr returns an int   ...

OK, maybe.
  • ... and I’m not using auto out of programming style momentum.
Does that change the meaning, beyond the other points mentioned? Or is this part redundant?

My point is not to present all the possible cases (I think there are only two), but to point out that the code: int x = expr; is ambiguous as to meaning (intent) and should never be written.

I’m pretty much with Herb here, except that I don’t like his use of “=“ when there is no assignment operator in use. I think this leads to confusing when teaching about operators.

There are two cases, what Herb calls “tracking” and “making the type stick.” I think I’d call them “tracking” and “fixing.” I think they should be spelled:

auto x{expr}; // tracking - we want x to be whatever expr returns, even if it is changed by maintenance
auto x{type{expr}}; // fixing - we want x to be type regardless of what expr returns.

I don’t see any other case.

The explicit/implicit distinction is a very important one because of the issues of implicit conversion, but I’m not convinced it applies here (although Howard’s presentation may yet convince me).

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."

To be clear, you are suggesting that the "I'm guessing it returns an int" case would just be auto x = expr; yes?

Probably. This is the problem with int x = expr; it isn’t clear what exactly it should be.

Or would some people still write the int{expr} because they (sometimes) want to be clear about the type info? ie "I want the reader to know this is an int, since it wasn't actually very clear in context, and it is important in this particular case"

It think it should be one of those two cases and until it is changed to one, some analysis it required to determine which is the appropriate one.

And, importantly, when the type of expr changes,are you sure that you still want it to be an int, with that conversion?

If not, then we are in the “tracking” case.

Now, int doesn't have explicit constructors so it is a bad example, but things like shared_ptr and chrono types do.
If you always write auto x = type{expr}, you lose the explicit/implicit distinction, thus losing a safety net that the committee thought was worth spending a keyword on.  In fact, most guidelines suggest explicit should be the default choice for constructors, yet this form of auto works against that guideline.

I don’t see that this is the case. You are making an affirmative decision about what type you use. You are not subject to implicit (hidden) conversions.

What is the value of

    auto x = int{expr};

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};

What about when I want it to be int regardless of the type of expr, unless that is an explicit conversion, because in that case I probably want to double-check.

Or what if I write auto x = type{expr} *precisely* because I want the explicit conversion?
Does that mean whenever I see auto x = type{expr} I'm left wondering "do they want the explicit conversion, or is this just their style for all occassions?"

You didn't comment on the examples which motivate my concerns:

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???
nanoseconds ns = duration; // if duration is strong chrono type, does the conversion you wanted/expected, if duration is int, fails to compile


auto sp = shared_ptr<Foo>(ptr);  // ptr is a weak_ptr, need shared
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.

Sorry that I didn’t comment on this. I’ve not spent time on these examples, because I assumed they are the one Howard presented here:

I spoke with him right after and I pointed out that the real solution here is to document these requirements of the function. Try to fix it with the initialization is, in my opinion, the wrong place to address the problem.

Be seeing you,