Help getting started

Sorry, was a bit tired I guess. I meant polymorphic lambdas (template
type parameters) - I wasn't intending to suggest some other construct
you hadn't suggested.

I'm still not sure I follow the ambiguity resolution. Are you saying
this named function template syntax would diverge from lambdas? (in the
sense that a single identifier specifies an unnamed parameter in a
lambda, but an untyped on in this syntax you're proposing)

It's not surprising that you didn't follow, because what I said didn't
make any sense (sorry). I guess I see the problem you're referencing, and I
don't know what the solution should be yet.

I think you're conflating two new language features that ought to be
considered in isolation:

1. Automatic polymorphization of parameter types.

2. Automatic deduction of return types for non-lambda functions.

Thinking about how to add feature 1 in isolation, in a way that works
for all functions, function templates, and lambda functions, without the
above-mentioned ambiguity between parameters without names and those
without types, the solution that first occurs to me is the one suggested
by Robert Frunzke in a comment on your C++Next article: use "auto" in
place of the type name rather than omitting it.

So we would write

auto min(auto x, auto y)->decltype(x < y ? x : y)
{ return x < y ? x : y; }

which is equivalent to

template <class T, class U>
auto min(T x, U y)->decltype(x < y ? x : y)
{ return x < y ? x : y; }

And then, independently of that, we also allow the "->decltype..." to be
omitted by following the same rules as for lambdas.

Indeed, one might even want to omit both the type and the name of the
parameter, e.g.

void ignore(auto&&...) {}

This also expands more easily to more general pattern-matching, such as:

void f(std::vector<auto> const&);

which can fill in the type in the same way that function templates do.

Though of course it's less clear what to do with:

void f(std::pair<auto, auto> const&);

(my instinct is that the two autos should spawn independent template
parameters, but one might argue that they should be the same type)

But this is now getting rather off-topic...

John Bytheway

Sorry, was a bit tired I guess. I meant polymorphic lambdas (template
type parameters) - I wasn't intending to suggest some other construct
you hadn't suggested.

I'm still not sure I follow the ambiguity resolution. Are you saying
this named function template syntax would diverge from lambdas? (in the
sense that a single identifier specifies an unnamed parameter in a
lambda, but an untyped on in this syntax you're proposing)

It's not surprising that you didn't follow, because what I said didn't
make any sense (sorry). I guess I see the problem you're referencing, and I
don't know what the solution should be yet.

I think you're conflating two new language features that ought to be
considered in isolation:

1. Automatic polymorphization of parameter types.

2. Automatic deduction of return types for non-lambda functions.

I agree that in principle they ought to be considered separately.
However, I want to prove that the terse syntax is achievable.

Thinking about how to add feature 1 in isolation, in a way that works
for all functions, function templates, and lambda functions, without the
above-mentioned ambiguity between parameters without names and those
without types, the solution that first occurs to me is the one suggested
by Robert Frunzke in a comment on your C++Next article: use "auto" in
place of the type name rather than omitting it.

So we would write

auto min(auto x, auto y)->decltype(x < y ? x : y)
{ return x < y ? x : y; }

which is equivalent to

template <class T, class U>
auto min(T x, U y)->decltype(x < y ? x : y)
{ return x < y ? x : y; }

And then, independently of that, we also allow the "->decltype..." to be
omitted by following the same rules as for lambdas.

These syntax choices should probably be available to users, but they
don't quite achieve the same goal of terseness, which is especially
important where lambdas are concerned.

Indeed, one might even want to omit both the type and the name of the
parameter, e.g.

void ignore(auto&&...) {}

This also expands more easily to more general pattern-matching, such as:

void f(std::vector<auto> const&);

which can fill in the type in the same way that function templates do.

Though of course it's less clear what to do with:

void f(std::pair<auto, auto> const&);

(my instinct is that the two autos should spawn independent template
parameters, but one might argue that they should be the same type)

...but one would be wrong to do so :slight_smile:

[FYI, this function is broken: ?: produces an lvalue if its second and third
arguments are both lvalues, and decltype of an lvalue (outside the
decltype(identifier) case) produces a reference. So this returns a reference
to a function argument.]

I'd be somewhat wary of overloading the meaning of 'auto' in this way. Another
likely proposal for 'auto' extension is allowing 'auto' anywhere within a
variable's type, and deducing the appropriate type from the initializer.
Hence:

auto (&min)(auto x, auto y) = std::min<int>;

The 'auto' in the parameter types here would naturally mean 'deduce this type
from the initializer'. Another potential source of confusion would be with
default arguments:

auto f(auto x = 0) { return x; }

Is this implicitly a function template, or does x have type 'int'?

Perhaps 'template' would be a better keyword to propose than 'auto' here?

auto min(template a, template b) -> std::remove_reference<decltype(a < b ? a :
b)> noexcept(noexcept(a < b ? a : b)) { return a < b ? a : b; }

... or more tersely ...

auto min(template a, template b) noexcept(auto) { return a < b ? a : b; }

- Richard

I think you’re conflating two new language features that ought to be
considered in isolation:

  1. Automatic polymorphization of parameter types.
  2. Automatic deduction of return types for non-lambda functions.

Thinking about how to add feature 1 in isolation, in a way that works
for all functions, function templates, and lambda functions, without the
above-mentioned ambiguity between parameters without names and those without
types, the solution that first occurs to me is the one suggested by Robert
Frunzke in a comment on your C++Next article: use “auto” in
place of the type name rather than omitting it.

So we would write

auto min(auto x, auto y)->decltype(x < y ? x : y) { return x < y ? x : y; }

which is equivalent to

template <class T, class U> auto min(T x, U y)->decltype(x < y ? x : y) {
return x < y ? x : y; }

[FYI, this function is broken: ?: produces an lvalue if its second and third
arguments are both lvalues, and decltype of an lvalue (outside the
decltype(identifier) case) produces a reference. So this returns a reference
to a function argument.]

I’d be somewhat wary of overloading the meaning of ‘auto’ in this way. Another
likely proposal for ‘auto’ extension is allowing ‘auto’ anywhere within a
variable’s type, and deducing the appropriate type from the initializer.
Hence:

auto (&min)(auto x, auto y) = std::min;

The ‘auto’ in the parameter types here would naturally mean ‘deduce this type
from the initializer’. Another potential source of confusion would be with
default arguments:

auto f(auto x = 0) { return x; }

Is this implicitly a function template, or does x have type ‘int’?

Perhaps ‘template’ would be a better keyword to propose than ‘auto’ here?

auto min(template a, template b) → std::remove_reference<decltype(a < b ? a :
b)> noexcept(noexcept(a < b ? a : b)) { return a < b ? a : b; }

… or more tersely …

auto min(template a, template b) noexcept(auto) { return a < b ? a : b; }

  • Richard

Hi Richard,

I really like the later syntax, very terse, however how does it address the binding temporary to reference issue ?

Do we rely on the compiler deducing that the reference is not adequate and removing it ?

As for the “same type” discussion (ie, do we consider that auto min(template a, template b) have similar or different types for a and b), I would simply rely on decltype:

auto min(template a, decltype(a) b) both a and b have the same type

auto min(template a, template b) a and b have possibly different type

I really wish this could get reality, the kludgy syntax has been a trademark of C++ for too long :slight_smile:

– Matthieu

I think you're conflating two new language features that ought to be
considered in isolation:

1. Automatic polymorphization of parameter types.
2. Automatic deduction of return types for non-lambda functions.

Thinking about how to add feature 1 in isolation, in a way that works
for all functions, function templates, and lambda functions, without the
above-mentioned ambiguity between parameters without names and those without
types, the solution that first occurs to me is the one suggested by Robert
Frunzke in a comment on your C++Next article: use "auto" in
place of the type name rather than omitting it.

So we would write

auto min(auto x, auto y)->decltype(x < y ? x : y) { return x < y ? x : y; }

which is equivalent to

template <class T, class U> auto min(T x, U y)->decltype(x < y ? x : y) {
return x < y ? x : y; }

[FYI, this function is broken: ?: produces an lvalue if its second and third
arguments are both lvalues, and decltype of an lvalue (outside the
decltype(identifier) case) produces a reference. So this returns a reference
to a function argument.]

Don't feel bad though, I made that mistake in the first version of my
article :wink:

I'd be somewhat wary of overloading the meaning of 'auto' in this way. Another
likely proposal for 'auto' extension is allowing 'auto' anywhere within a
variable's type, and deducing the appropriate type from the initializer.
Hence:

auto (&min)(auto x, auto y) = std::min<int>;

[FYI, that code is technically nonportable because you can't know
whether std::min has additional, defaulted arguments. :wink: ]

The 'auto' in the parameter types here would naturally mean 'deduce this type
from the initializer'. Another potential source of confusion would be with
default arguments:

auto f(auto x = 0) { return x; }

Is this implicitly a function template, or does x have type 'int'?

It's implicitly a function template. However, making the truth of that
obvious requires some reform of C++ parameter passing in general, which
is a totally different topic (about which I have ideas).

Perhaps 'template' would be a better keyword to propose than 'auto' here?

auto min(template a, template b) -> std::remove_reference<decltype(a < b ? a :
b)> noexcept(noexcept(a < b ? a : b)) { return a < b ? a : b; }

... or more tersely ...

auto min(template a, template b) noexcept(auto) { return a < b ? a : b; }

No offense, but that is way too far from what I am trying to accomplish
to be of much interest to me. I'm sticking with

[]min(a, b) { return a < b ? a : b; }

:slight_smile:

I'd be somewhat wary of overloading the meaning of 'auto' in this way.
Another
likely proposal for 'auto' extension is allowing 'auto' anywhere within a
variable's type, and deducing the appropriate type from the initializer.
Hence:

auto (&min)(auto x, auto y) = std::min<int>;

[FYI, that code is technically nonportable because you can't know
whether std::min has additional, defaulted arguments. :wink: ]

That provision only applies to member functions; see 17.6.5.4/3 for the
non-member case :slight_smile:

Perhaps 'template' would be a better keyword to propose than 'auto' here?

auto min(template a, template b) -> std::remove_reference<decltype(a < b ?
a : b)> noexcept(noexcept(a < b ? a : b)) { return a < b ? a : b; }

... or more tersely ...

auto min(template a, template b) noexcept(auto) { return a < b ? a : b; }

No offense, but that is way too far from what I am trying to accomplish
to be of much interest to me. I'm sticking with

[]min(a, b) { return a < b ? a : b; }

I wasn't intending to suggest you do any differently -- I was commenting on
the proposal to use 'auto' as a parameter type for creating an implicit
template, not your proposal (which I quite like).

- Richard

I'd be somewhat wary of overloading the meaning of 'auto' in this way.
Another
likely proposal for 'auto' extension is allowing 'auto' anywhere within a
variable's type, and deducing the appropriate type from the initializer.
Hence:

auto (&min)(auto x, auto y) = std::min<int>;

[FYI, that code is technically nonportable because you can't know
whether std::min has additional, defaulted arguments. :wink: ]

That provision only applies to member functions; see 17.6.5.4/3 for the
non-member case :slight_smile:

Curses! I'm hoist with my own petard!

Perhaps 'template' would be a better keyword to propose than 'auto' here?

auto min(template a, template b) -> std::remove_reference<decltype(a < b ?
a : b)> noexcept(noexcept(a < b ? a : b)) { return a < b ? a : b; }

... or more tersely ...

auto min(template a, template b) noexcept(auto) { return a < b ? a : b; }

No offense, but that is way too far from what I am trying to accomplish
to be of much interest to me. I'm sticking with

[]min(a, b) { return a < b ? a : b; }

I wasn't intending to suggest you do any differently -- I was commenting on
the proposal to use 'auto' as a parameter type for creating an implicit
template, not your proposal (which I quite like).

Ah, thanks for the clarification and for the complement.