C++ Logo


Advanced search

Re: [std-proposals] [Resurrected Proposal] Concept introduces a typename

From: Barry Revzin <barry.revzin_at_[hidden]>
Date: Tue, 2 May 2023 21:27:16 -0500
On Tue, May 2, 2023 at 11:37 AM Gašper Ažman via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I'd be strongly opposed to introducing a yet another syntax to introduce
> dependent names. There's nothing wrong with current template-heads.

+1. Over time I've come to the position that the terse syntax (specifically
Concept auto, not auto by itself) was not a good idea. It's just pretty
rare that it's even viable - you pretty regularly need the type, have a
multi-parameter constraint, want multiple parameters of the same type, or
have a constraint that you can't express using the terse syntax.
Occasionally I'll see something in our codebase that takes a "std::integral
auto val" or something, and be mildly shocked that it worked.

If we want to cut some characters, there is room for improvement in the
longer syntax.

To start with, the leading template keyword is unnecessary. This was
pointed out during the terse concepts discussions. Declarations can't start
with <, so it's unambiguous to both the compiler and the reader what this

<class T> auto min(T const&, T const&) -> T const&;

That's 9 (or 8, if you don't put the space) characters gone, with the added
benefit that it starts becoming reasonable to fit a whole declaration on
one line. This is still longer than the proposed syntax, but only
marginally so:

<Sortable S> void f(S x);
void f(Sortable S x);

And doesn't lead to any additional questions about what reusing the same
identifier means in multiple parameters. The paper says:

[](Number auto x, decltype(x) y) { } //< current
[](Number N x, N y) { }` //< **proposed**

Are those supposed to be equivalent? The former allows a call (1, 2L), the
latter seems like it would require both parameters to be the same type,
which we can already write in marginally more characters today:

[](Number N x, N y)
[]<Number N>(N x, N y)

What I'm suggesting just allows the same in function templates:

auto gcd(integral T l, T r) - T;
<integral T> auto gcd(T l, T r) -> T;

Next, I would like to make the following claims:

   1. Type template parameters are *overwhelmingly* more common than
   non-type or template template parameters.
   2. Anonymous non-type template parameters are not especially valuable to
   support, and with the coming addition of _ as a placeholder, are a waste of
   syntax space.

Put those together, and it offers the ability to drop typename/class. It's
one of those weird things in C++ where we have two exact synonyms, so
people have pointless (in the grand scheme of things) style discussions
about which to use - typename conveys that it's any type, but class is
shorter. Well, maybe just put neither:

<T> auto min(T const&, T const&) -> T const&;

The reason I bring up (2) is that today this could've meant an unnamed
value of type T. It's not possible today, because you can't omit the
template keyword today, but a similar transformation in lambdas is valid
code today (well, at least writing <T> could be, it falls apart quickly
after that):

auto min = []<T>(T const& x, T const& y) -> T const& { ... }

Omitting a name for a non-type template parameter is even less useful in
lambdas than in function templates. You could *only* call it as
f.operator()<1>() anyway? Making the user write that mess just to ignore
the value seems very rude. Maybe we could reclaim the syntax.

The reason for dropping class/typename is to also suggest another, more
adventurous direction. Changing which side of the type we put the
constraint on. That is:

<T: integral> auto gcd(T l, T r) -> T;
<V: view + forward_range> class some_range_adapter { ... };

There are two, unrelated motivations for this ordering:

First, It allows putting multiple constraint-ids in there, which does come
up a lot, and it's a pretty clear way of doing so. We can fight over
whether the grouping should be + or &&, or split the difference and call it

Second, As more code moves to C++20, template-heads are becoming more
ambiguous to the reader. Here's an example, what does this mean:

template <Some Thing>

What is Thing?

   - A type, constrained on the concept named Some?
   - A value, whose type is Some?
   - A value, whose type is some specialization of the class template Some,
   whose type will be deduced by CTAD?

Non-type template parameters, now that they can be class types too, are
going to steadily become more and more common. It'd be nice to have some
differentiation for the reader.

But even if we don't do this last part, dropping template/class/typename
seems pretty nice.


Received on 2023-05-03 02:27:30