Date: Tue, 16 Feb 2021 12:06:14 -0500
>
> 3. All the dots in the pack expansion stuff are pretty hard to read; I
>> think more consideration/justification/explanation of that syntax is
>> warranted. In particular I don’t understand the need to “nominate” a pack
>> for expansion; I see the footnote about some compilers needing to be
>> alerted beforehand about whether or not to keep the tokens that follow, but
>> I don’t understand that, nor have much sympathy for those compilers :) —
>> better the compilers deal with confusion than the user.
>>
>
> Even for compilers that are "better", you need a way of designating a
> subexpression of, say an initializer-clause as being something that can be
> expanded. Packs are declared. Other terms that might be expanded are not.
> Our starting point was just adding ... before that subexpression, just like
> we do with pack declarations (points for consistency).
>
> We've gone through several iterations of this idea (along with Barry
> Revzin, who's written a bunch of pack-related proposals). Eventually, we
> realized that trying to specify exactly which subexpresion was a pack was
> kind of meaningless and that we should just move the "pack operator" all
> the way to the left of the initializer clause. It makes the entire
> expression a pattern for expansion, so we get:
>
> f(... /*expression containing things that may be packs */ ...);
>
> Which, despite having a lot of dots, is actually a really general,
> consistent and IMO elegant solution to all of the problems we've run into
> trying to figure out how this should work.
>
> And if you actually read the paper, there's discussion about dropping the
> trailing ellipsis when using a leading ellipsis because you can't have an
> unexpanded pattern.
>
>
> That bit leads to still more confusion. So the idea, were we to drop the
> trailing ellipsis, is that reflected pack expansions will be expanded with
> a leading ellipsis? I.e. the exact reverse of every other situation?
>
Surely you can see how this warrants further justification, such potential
> for confusion can’t just be accepted under the rationale you’ve given.
>
Yes, it is possible that splices of ranges can be expanded with just a
leading ellipsis.
It's not "the exact reverse". The idea of expanding something other than a
pack (possibly not even dependent) is considerably different than expanding
a conventional pack. I don't think it's unusual to have different operators
for doing different things. Maybe this would be a better model:
f(... expr_involving_spliced_range) // may not be dependent, does not
contain conventional packs
f(expr_involving_argument_pack ...) // is dependent, contains only
conventional packs
f(... expr_involving_both ...) // contains both packs and spliced ranges
In the last case, our current model mandates two expansions, so you need
two ellipses. (The precedence is even right. The postfix ellipsis applies
first, then the prefix ellipsis).
So, walk me through why certain compiler can’t handle this, or identify
> where I’m misunderstanding:
>
> ```
> template<typename... Args>
> auto f(Args... args) {
> auto newArgsRefl = somemetafunc(^Args);
> return g(static_cast<[:newArgsRefl:]>(args)...);
> }
> ```
>
What do you expect to happen? You're certainly expanding over args. If
newArgsRefl is a vector, then we want this to be ill-formed. Because this
is in a template, a compiler (all compilers?) could perform the expansion,
but there are two reasons it shouldn't. First, it might have been a typo
and the programmer didn't intend for the range to expand. The programmer
should be encouraged to indicate that the splice will be expanded. Second,
the same notation isn't possible outside of templates because of
implementation issues. Whether you like it or not, that's going to be a
limiting factor for any proposals to expand non-packs.
If you write this:
return g(static_cast<... [:newArgsRefl:]>(args)...);
The intent is obvious, and the prefix ... has the same meaning in all
contexts.
Andrew
> 3. All the dots in the pack expansion stuff are pretty hard to read; I
>> think more consideration/justification/explanation of that syntax is
>> warranted. In particular I don’t understand the need to “nominate” a pack
>> for expansion; I see the footnote about some compilers needing to be
>> alerted beforehand about whether or not to keep the tokens that follow, but
>> I don’t understand that, nor have much sympathy for those compilers :) —
>> better the compilers deal with confusion than the user.
>>
>
> Even for compilers that are "better", you need a way of designating a
> subexpression of, say an initializer-clause as being something that can be
> expanded. Packs are declared. Other terms that might be expanded are not.
> Our starting point was just adding ... before that subexpression, just like
> we do with pack declarations (points for consistency).
>
> We've gone through several iterations of this idea (along with Barry
> Revzin, who's written a bunch of pack-related proposals). Eventually, we
> realized that trying to specify exactly which subexpresion was a pack was
> kind of meaningless and that we should just move the "pack operator" all
> the way to the left of the initializer clause. It makes the entire
> expression a pattern for expansion, so we get:
>
> f(... /*expression containing things that may be packs */ ...);
>
> Which, despite having a lot of dots, is actually a really general,
> consistent and IMO elegant solution to all of the problems we've run into
> trying to figure out how this should work.
>
> And if you actually read the paper, there's discussion about dropping the
> trailing ellipsis when using a leading ellipsis because you can't have an
> unexpanded pattern.
>
>
> That bit leads to still more confusion. So the idea, were we to drop the
> trailing ellipsis, is that reflected pack expansions will be expanded with
> a leading ellipsis? I.e. the exact reverse of every other situation?
>
Surely you can see how this warrants further justification, such potential
> for confusion can’t just be accepted under the rationale you’ve given.
>
Yes, it is possible that splices of ranges can be expanded with just a
leading ellipsis.
It's not "the exact reverse". The idea of expanding something other than a
pack (possibly not even dependent) is considerably different than expanding
a conventional pack. I don't think it's unusual to have different operators
for doing different things. Maybe this would be a better model:
f(... expr_involving_spliced_range) // may not be dependent, does not
contain conventional packs
f(expr_involving_argument_pack ...) // is dependent, contains only
conventional packs
f(... expr_involving_both ...) // contains both packs and spliced ranges
In the last case, our current model mandates two expansions, so you need
two ellipses. (The precedence is even right. The postfix ellipsis applies
first, then the prefix ellipsis).
So, walk me through why certain compiler can’t handle this, or identify
> where I’m misunderstanding:
>
> ```
> template<typename... Args>
> auto f(Args... args) {
> auto newArgsRefl = somemetafunc(^Args);
> return g(static_cast<[:newArgsRefl:]>(args)...);
> }
> ```
>
What do you expect to happen? You're certainly expanding over args. If
newArgsRefl is a vector, then we want this to be ill-formed. Because this
is in a template, a compiler (all compilers?) could perform the expansion,
but there are two reasons it shouldn't. First, it might have been a typo
and the programmer didn't intend for the range to expand. The programmer
should be encouraged to indicate that the splice will be expanded. Second,
the same notation isn't possible outside of templates because of
implementation issues. Whether you like it or not, that's going to be a
limiting factor for any proposals to expand non-packs.
If you write this:
return g(static_cast<... [:newArgsRefl:]>(args)...);
The intent is obvious, and the prefix ... has the same meaning in all
contexts.
Andrew
Received on 2021-02-16 11:06:25