C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Proposal to allow multiple template parameters packs of different types

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Tue, 15 Nov 2022 10:43:36 -0500
On Mon, Nov 14, 2022 at 1:08 PM Anoop Rana <ranaanoop986_at_[hidden]> wrote:

> > Ts=<int,int>, Us=<> , but as far as I could tell, Anoop rejects that
> outcome.
>
> Note that I don't actually reject `Ts={int, int}`, `Us={ }`. What I am
> objecting is that the program 1.1 is ill-formed *with `U` not being { } *as
> per the current wording instead of being
>
> *1)* well-formed with `Ts={int, int}`, `Us={ }` assuming packs are
> greedy(no ambiguity) or
> *2) *ill-formed with `Ts={int, int}`, `Us={ }` assuming this is ambiguous
> instead of being greedy or
> *3) *ill-formed with `Ts={int, int}`, `Us={}` assuming packs are
> greedy(no ambiguity). If this point 3 is what the standard currently
> supports then it doesn't make sense because a parameter pack is allowed to
> be empty so that Ts={int, int}, Us={ } is an expected output and the
> program should be well-formed instead of ill-formed. Note again that
> Ts={int, int}, Us={ } is an expected output in this point 3 because there
> is no ambiguity interpretation used here in this point. These
> interpretations are separate from each other as also noted below.
>

I think your classification into 1,2,3 doesn't make sense; but I've also
just realized that I was wrong about the current state of affairs.

    template<class... Ts, class... Us> void f();
    f<int,int>();

is actually accepted today by both GCC and Clang (deducing Ts={int,int},
Us={}).
MSVC rejects the template declaration of `f` itself (even before it's ever
called) with a hard error. I think MSVC's behavior is what the Standard
strictly requires.
But this is highly relevant to your paper, because "Let's change the rules
that everyone currently follows" is a harder sell than "Let's change the
rules that 2/3 of vendors don't follow anyway." And ideally you'd be saying
"Let's change the rules to *the thing that 2/3 of vendors already do*"
(i.e., "standardize existing practice"). I don't think that's actually
what you're saying, but, maybe it should be?

As before, the paper lacks actual motivation for why a working programmer
would want this change (or any change in this area). The proposal increases the
number of paint splotches that are valid C++ programs
<https://www.mcmillen.dev/sigbovik/2019.pdf>, but what is the *actual*
benefit to a human C++ programmer?

Instead of spreading out your "before/after comparison" section vertically
over three pages, I suggest you make a little table something like this:

|----------------------------------------------------------|-----------------|-----|-------|------|------|----------|

| Template | Usage
  | GCC | Clang | MSVC | EDG | Standard |

|----------------------------------------------------------|-----------------|-----|-------|------|------|----------|

| template<class... Ts, class... Us> int f1(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, class... Us> int f1(); | f1<int, int>
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int... Us> int f2(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int... Us> int f2(); | f2<int, 1>
  | Err | Err | Err | Err | Err |

| template<class... Ts, int U> int f3(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int U> int f3(); | f3<int, 1>
  | Err | Err | Err | Err | Err |

| template<class... Ts, template<class> class U> int f4(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, template<class> class U> int f4(); | f4<int, C>
  | Err | Err | Err | Err | Err |

| template<class... Ts, int U, class... Vs> int f5(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int U, class... Vs> int f5(); | f5<int, 1>
  | Err | Err | Err | Err | Err |

| template<class... Ts, int U, class... Vs> int f5(); | f5<1, int>
  | Err | Err | Err | Err | Err |

| template<class... Ts, int U, class... Vs> int f5(); | f5<int, 1,
int> | Err | Err | Err | Err | Err |

| template<class... Ts, int... Us, class... Vs> int f6(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int... Us, class... Vs> int f6(); | f6<int>
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int... Us, class... Vs> int f6(); | f6<int, int>
  | ✓ | ✓ | Err | Warn | Err |

| template<class... Ts, int... Us, class... Vs> int f6(); | f6<int, 1>
  | Err | Err | Err | Err | Err |

| template<class... Ts, int... Us, class... Vs> int f6(); | f6<int, 1,
int> | Err | Err | Err | Err | Err |

| template<auto... Ts, int... Us> int f7(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<auto... Ts, int... Us> int f7(); | f7<1>
  | ✓ | ✓ | Err | Warn | Err |

| template<auto... Ts, int... Us> int f7(); | f7<&i, 1>
  | ✓ | ✓ | Err | Warn | Err |

| template<int... Ts, auto... Us> int f8(); |
  | ✓ | ✓ | Err | Warn | Err |

| template<int... Ts, auto... Us> int f8(); | f7<1>
  | ✓ | ✓ | Err | Warn | Err |

| template<int... Ts, auto... Us> int f8(); | f7<&i>
  | Err | Err | Err | Err | Err |

| template<same_as<char> auto... Ts, same_as<int> auto... Us> int f9(); |
          | ✓ | ✓ | Err | Unsupp | Err |

| template<same_as<char> auto... Ts, same_as<int> auto... Us> int f9(); |
f9<1> | Err | Err | Err | Unsupp | Err |

| template<same_as<char> auto... Ts, same_as<int> auto... Us> int f9(); |
f9<'x', 1> | Err | Err | Err | Unsupp | Err |

and then add another column on the end, "Proposed," with what you propose
for each case.

*NOTE:*
> Just to sum up some things, according to the paper `template<typename...
> T, typename... U> f(){}` is ill-formed because `U` and `T` have the same
> underlying types(meaning they both are "type" parameter packs)
>

FWIW, I'd say "the same kind" rather than "the same underlying type," as
the latter has a technical meaning already.
I'd like to see an example where the parser's kind-disambiguation rules
come into play, like
    constexpr double PI = 3.14; // red herring
    template<char... Ts, class... Us> int f10(); // currently ill-formed,
proposed OK
    f10<char{}>(); // proposed OK with Ts={0} Us={}
    f10<char()>(); // proposed OK with Ts={} Us={char()}
    f10<char(int(3.14))>(); // proposed OK with Ts={3} Us={}
    f10<char(int(PI))>(); // proposed OK with Ts={} Us={char(int)}

A procedural note: You've labeled your mailing-list drafts PxxxxR0 and
PxxxxR1. You should instead label them DxxxxR0 (draft revision 0) and
DxxxxR0 (draft revision 1), and so on. When (if) you submit the paper to an
actual mailing, you'll number *that submission* as PxxxxR0, and then start
working on new drafts as DxxxxR1 (draft revision whatever).

HTH,
Arthur

>

Received on 2022-11-15 15:43:48