One more syntax proposal that extends P1985's unkiversal template parameter: `template auto`

1. allow `template auto` in any deduced context, for example `vector<template auto> vec = get_vec();`
2. use the syntax `template<class T> auto` to introduce the name `T`.

template<class T> auto x = foo()
vector<template<class V> auto> v = get_vec()

Some extra stuff:
* Introducing a name is optional, you can use `template<class> auto` for a type placeholder, instead of a universal one
* you can have nttp or template placeholders too

std::array<string, template<size_t arr_size> auto> arr = get_arr();

A bit verbose compared to my previously proposed auto<class T>. Honestly, I like that more, but I could live with this.

I definitely wouldn't want a syntax where you are required to immediately repeat the introduced name.


On 30 September 2022 15:01:58 BST, Edward Catmur via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

On Fri, 30 Sept 2022 at 14:19, Oleksandr Koval <oleksandr.koval.dev@gmail.com> wrote:
Sorry if this was already discussed, I like `auto<class T>` syntax but the problem I see is this:

std::vector<int> f();
auto<class T> v = f(); // should T be `std::vector<int>` or just `int`?

That syntax would work as a 2-step process: first you declare entities, then deduce them. The declaration and deduction should be pretty much the same as for (non-terse) function templates.

So T is repeated:

auto<class T> T v = f();


auto<class T> std::vector<T> v = f();


auto<template<class> class TT, class T> TT<T> v = f();

It’s a bit confusing when you see them side by side. Because of that I don’t think that using angle brackets is a good idea (at least I can’t find any form which is not confusing). We need a new syntax which will clearly show that typename(s) is closely related to `auto` itself. For example:

auto:[class T] v = f();
auto:[class T1, classT2] [key, value] = get_pair();

On Fri, Sep 30, 2022 at 4:07 AM Edward Catmur via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

On Thu, 29 Sept 2022 at 21:44, Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Thu, Sep 29, 2022 at 3:57 PM Lénárd Szolnoki via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On 29 September 2022 20:25:06 BST, Edward Catmur <ecatmur@googlemail.com> wrote:
>On Thu, 29 Sept 2022 at 18:15, Lénárd Szolnoki via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
>> I think this could be addressed by two distinct proposals.
>> 1. allow placeholders to appear in any deduced context (std::vector<auto>)
>> 2. allow a placeholder to introduce a name (auto<class T>, auto<int i>
>> might appear as a deduced nttp)
>> Then you can have your vector<auto<class T>> to deduce the value type and
>> introduce the name T for that.
>std::vector<auto> is problematic, because elsewhere auto means a value of
>unconstrained type (e.g. in template<auto>).

I disagree. auto is a placeholder for the type of the non-type parameter. If you don't omit the name, then that name refers to the value.

FWIW, I tend to agree with Lénárd here: `auto *p, std::unique_ptr<auto> q` seems quite reasonable to me.

Ye-esss; looking at the grammar; `auto` is always a placeholder-type-specifier, which is a simple-type-specifier, so it makes sense that it takes the place of a concrete (or inferred) type. 

So Lénárd, I apologise; you're correct that in these contexts `auto` designates a type, not a value. I'm still trying to get my head round "template<auto> int f();", but the syntax is clear.

However, I foresee practical problems with allowing auto anywhere in a declaration. Consider
    template<class T> void f(T t);  // since C++98
    template<class T> void g(decltype(T(1)) t);  // since C++11
    void f(auto t);  // since C++20, equivalent to f #1
    void g(decltype(auto(1)) t);  // since C++23, not equivalent to g #1

Now, the `T` parameter to `g` is not deducible, so "obviously" the `auto` in g #2 doesn't mean the same thing as the `auto` in f #2. But are you sure we can teach that to the computer?

(Background: I teach that `auto` since C++14 has (like most C++ keywords) had two meanings: concrete type inference, as in auto x = 1, and templatey type deduction, as in [](auto x){}. The physical mechanisms behind, and consequences of, these two usages of `auto` are vastly different, although their human-level meaning is similar: "I don't want to bother with types; compiler, please figure it out." So if you see me talking about "inference" versus "deduction," or "the first meaning of auto" versus "the second meaning of auto," that's what I'm talking about.)

The deduction process is ultimately the same; both end up in temp.deduct.call. In an abbreviated function template or generic lambda, the deduction can be overridden by explicit template argument, but that's a relatively minor effect. And syntactically, they're the same; a decl-specifier of a decl-specifier-seq (of a function parameter or variable declaration); `auto` in a function return type or trailing return type has less in common, though I'd suppose you'd class that as inference. Still, I guess it's OK to teach it that way.

It would certainly not be reasonable to have a rule like "Try to interpret every `auto` as deduction, but if that would result in a non-deducible template parameter, then backtrack and assume it's inference instead."  It would be reasonable to have a rule that boils down to "Inside the operand of a decltype or sizeof or array bound, the `auto` always means inference not deduction." But is `g` above the only problem case?  Are there other corner cases where `auto` is already legal today, and/or we wouldn't want it to mean a template parameter?
    void f(A<auto(int())>);  // is this concrete A<0> or templatey A<T(int())>? I guess the type of A will disambiguate...

Currently a placeholder can appear as the decl-specifier of a function parameter, variable declaration, or template parameter or, as exactly one simple-type-specifier of a return or trailing return type, conversion function id, new expression's type id, or (`auto` only) as the type specifier of a functional cast. If these were relaxed, I think we'd probably be OK; you just go through inventing extra type template parameters and perform deduction as usual.

Also I don't think ambiguity *can* be an issue, or it would be already; there must be disambiguators in the syntax. Indeed, I have a strong suspicion that your `A<auto(int())>` is a most vexing parse.

Also consider that while
    void h1(std::vector<auto, auto> v);
would work fine with Lénárd's proposed syntax,
    void h2(std::array<auto, ??> a);
would not. That does seem mildly problematic. However, maybe it's consistent with C++20, which permits
    template<class T> concept Integral = true;
    template<Integral T> void ij();  // since C++20
but not
    template<int N> concept Odd = true;
    template<Odd N> void ij();  // error: Odd does not constrain a type

Yes, fair enough; this does conform to the grammar. 
Std-Proposals mailing list

Oleksandr Koval.