C++ Logo

std-discussion

Advanced search

Re: The issue about reference collapsing for function parameter pack

From: Andrew Schepler <aschepler_at_[hidden]>
Date: Mon, 7 Sep 2020 10:23:53 -0400
On Sun, Sep 6, 2020 at 10:01 AM jim x <xmh970252187_at_[hidden]> wrote:

> ...
>
"typename...T" is not a pack expansion, so [temp.variadic#7]
> <https://timsong-cpp.github.io/cppwp/n4659/temp.variadic#7> can't apply
> to this declaration which declare a template parameter pack.
>

Aha, I missed that. So yes, I guess this is an issue. When the pack
expansion "T&& ...args" is instantiated, clause [temp.variadic]/7
<https://timsong-cpp.github.io/cppwp/n4659/temp.variadic#7.1> does say T is
replaced with a template type parameter in each element of the function
parameter list. We would like to say that every template type parameter
(which is not a template template parameter) is always a *typedef-name*.
But since this template parameter was invented for the variadic template
instantiation, either it has no declaration in the source and no identifier
for [temp.param]/3 <https://timsong-cpp.github.io/cppwp/n4659/temp#param-3>
to apply to, or else "its identifier" is the "T" in "typename... T", which
as you point out does not make it a *typedef-name*.

I'll also retract my very first statement, "The name T of the template
parameter pack certainly isn't a *typedef-name*". I'm not really sure now
why the wording about the ellipsis is even there in [temp.param]/3. A
non-type template parameter doesn't need it, as [temp.param]/6
<https://timsong-cpp.github.io/cppwp/n4659/temp#param-6> says simply "A
non-type non-reference template-parameter is a prvalue" without
consideration for an ellipsis or template parameter pack. When looking at
just the template declaration, we don't know how many template parameters
an instantiation of a template parameter pack will have, but that's very
much like not knowing what the actual aliased type, aliased template,
referenced entity, or constant expression value will be for a non-pack
template parameter. This doesn't need to stop the pack's name from being a
*typedef-name*, *template-name*, lvalue, or prvalue. As far as needing to
actually be expanded, that's a separate rule (specifically, in
[temp.variadic]/6
<https://timsong-cpp.github.io/cppwp/n4659/temp.variadic#6>).


> And for this [temp.param#3]
> <https://timsong-cpp.github.io/cppwp/n4659/temp.param#3> rule, I have a
> different understanding with yours,
> >A type-parameter whose identifier does not follow an ellipsis defines
> its identifier to be a typedef-name (if declared without template) or
> template-name (if declared with template) in the scope of the template
> declaration. [ Note: A template argument may be a class template or alias
> template.
>
> This rule is totally about the parameter declaration. In other words,
> `template<typename T>` where identifier `T` is defined as a typedef-name.
> Conversely, For the form `template<typename...T>` , where T is not defined
> as a typedef-name, because such declaration does not satisfy what the above
> rule says, that is " A type-parameter whose identifier does not follow an
> ellipsis defines its identifier to be a typedef-name (if declared without
> template)". So, wherever such an identifier appear in, such an identifier
> can't be considered as a typedef-name(it's determined by the declaration
> where such a identifier appears). Such as, for this declaration `typedef
> int Integer`, `Integer` is a typedef-name whatever the form where the such
> a name appears.(Because the declaration declare such a name as a
> typedef-name). But, I'm not 100% sure the exactly intent of
> [temp.param#3] <https://timsong-cpp.github.io/cppwp/n4659/temp.param#3> is
> not what you said, I have to say whether a name has a property or not is
> determined by its declaration which introduced the name and such a property
> appertains to such a name , whatever how to use this name.
>
>
>
> Andrew Schepler <aschepler_at_[hidden]> 于2020年9月5日周六 下午8:18写道:
>
>> Oh, I think I missed a key point in your reading.
>>
>> such as, the deduced template argument is `int&` when I pass a lvalue of
>>> type `int` as the unique argument, that means, the collection of `pack
>>> expansion parameters` only has one element which is `int&`,
>>
>>
>> In the instantiation of "T&& ...args" when the template argument list is
>> ("int&"), the parameter T is not replaced with the template argument "
>> int&". It is replaced with the first (only) template parameter formed by
>> the instantiation of "<typename... T>". That template parameter is a
>> *typedef-name* which aliases int&.
>>
>> On Sat, Sep 5, 2020 at 8:12 AM Andrew Schepler <aschepler_at_[hidden]>
>> wrote:
>>
>>> Hi again Jim,
>>>
>>> It is the declaration of a template type parameter, not any of its uses,
>>> which makes it a *typedef-name*. Note the quoted rule "A
>>> *type-parameter* whose identifier does not follow an ellipsis defines
>>> its *identifier* to be a *typedef-name* (if declared without template)
>>> or *template-name* (if declared with template) in the scope of the
>>> template declaration." is [temp.param]/3, and [temp.param] is entirely
>>> about interpretation of the template parameters which appear in a "template
>>> <*template-parameter-list*>" syntax.
>>>
>>> Taking the packs out of the picture, in
>>>
>>> template <typename T>
>>> void func2(T&& arg) {}
>>>
>>> the identifier which does not follow an ellipsis is just the first
>>> appearance of T, in the parameter declaration. Since it does not, T
>>> becomes a *typedef-name* in the scope of the template; in particular,
>>> where it is named in "T&& arg".
>>>
>>> (Even if the rule did apply to the function parameter pack "T&& ...args",
>>> the identifier of the *type-parameter* is still T, not args, and T does
>>> not "follow an ellipsis" in this syntax at all.)
>>>
>>> Andrew Schepler
>>>
>>>
>>> On Sat, Sep 5, 2020 at 4:33 AM jim x <xmh970252187_at_[hidden]> wrote:
>>>
>>>> Hi, Andrew Schepler.
>>>>
>>>> Maybe you misread these rules. For my example, namely `T&&...args`, the
>>>> pack expansion occurs in a function parameter pack, that is said, the rule
>>>> [temp.variadic#4.1]
>>>> <https://timsong-cpp.github.io/cppwp/n4659/temp.variadic#4.1> applies
>>>> to this example.
>>>> That means, "the pattern is the parameter-declaration without the
>>>> ellipsis."(namely T&& args) . In addition, what the exactly meaning of "The
>>>> instantiation of a pack expansion that is neither a sizeof... expression
>>>> nor a fold-expression produces a list of elements E_1, E_2, ..., E_N, where
>>>> N is the number of elements in the pack expansion parameters... if the pack
>>>> is a template parameter pack" is that, for `T&&...args` where `T` is a
>>>> template parameter pack, it's a pack expansion. As aforementioned , the
>>>> pattern for `T&&...args` is `T&& args`, such form is the pattern for `Ei`
>>>> which would be generated by instantiating such a pattern. Note though,
>>>> this sentence "and replacing each pack expansion parameter with its i-th
>>>> element.", that means, the element is the corresponding value of the
>>>> template parameter as said in [temp.variadic#7.1]
>>>> <https://timsong-cpp.github.io/cppwp/n4659/temp.variadic#7.1>. such
>>>> as, the deduced template argument is `int&` when I pass a lvalue of type
>>>> `int` as the unique argument, that means, the collection of `pack
>>>> expansion parameters` only has one element which is `int&`, and such a
>>>> element is used to replace the corresponding pack expansion parameter which
>>>> is a component of the pattern. So the result of such an instantiation of
>>>> the pattern would be `int&&& args1`(hypothetical form), then the reference
>>>> collapsing should be performed for such a declaration. However within this
>>>> hypothetical declaration, `int&` is neither a *typedef-name or *
>>>> *decltype-specifier.* It violates the rule [dcl.ref#6]
>>>> <https://timsong-cpp.github.io/cppwp/n4659/dcl.ref#6>. So, I don't
>>>> think these rule is ok.
>>>>
>>>> Andrew Schepler <aschepler_at_[hidden]> 于2020年9月4日周五 下午6:43写道:
>>>> >
>>>> > I think the Standard is okay here.
>>>> >
>>>> > The name T of the template parameter pack certainly isn't a
>>>> typedef-name: we can't have "T var;" or "T&& var;". It's only inside a pack
>>>> expansion where T should obey this rule, as in (T&&... args) in this
>>>> example, or std::tuple<T&&...> var;, or etc.
>>>> >
>>>> > And I think the rules for instantiating pack expansions in
>>>> [temp.variadic] do qualify T inside an expansion for the reference
>>>> collapsing rule.
>>>> >
>>>> > [temp.variadic]/(5.3):
>>>> >>
>>>> >> In a template parameter pack that is a pack expansion ([temp.param]):
>>>> >>
>>>> >> if the template parameter pack is a type-parameter; the pattern is
>>>> the corresponding type-parameter without the ellipsis.
>>>> >
>>>> >
>>>> > [temp.variadic]/8:
>>>> >>
>>>> >> The instantiation of a pack expansion that is neither a sizeof...
>>>> expression nor a fold-expression produces a list of elements E_1, E_2, ...,
>>>> E_N, where N is the number of elements in the pack expansion parameters.
>>>> Each E_i is generated by instantiating the pattern and replacing each pack
>>>> expansion parameter with its i-th element. Such an element, in the context
>>>> of the instantiation, is interpreted as follows:
>>>> >>
>>>> >> if the pack is a template parameter pack, the element is a template
>>>> parameter ([temp.param]) of the corresponding kind (type or non-type)
>>>> designating the i-th corresponding type or value template argument;
>>>> >
>>>> > So when the template parameter pack is instantiated in func<int,
>>>> char>, the pattern is "typename T", then T is replaced with the invented
>>>> type template parameters E_i. We can either interpret "its identifier" for
>>>> such a parameter E_i to be the T in the pattern, or consider each E_i to
>>>> act as if it has a unique unspellable identifier since it replaces T.
>>>> Either way the identifier does not follow an ellipsis, so E_i is a
>>>> typedef-name.
>>>> >
>>>> > On Wed, Sep 2, 2020 at 8:49 AM jim x via Std-Discussion <
>>>> std-discussion_at_[hidden]> wrote:
>>>> >>
>>>> >> About the reference collapsing, its rule is defined as the
>>>> following:
>>>> >>
>>>> >> >If a typedef-name ([dcl.typedef], [temp.param]) or a
>>>> decltype-specifier denotes a type TR that is a reference to a type T, an
>>>> attempt to create the type “lvalue reference to cv TR” creates the type
>>>> “lvalue reference to T”, while an attempt to create the type “rvalue
>>>> reference to cv TR” creates the type TR.
>>>> >>
>>>> >> However, consider such a case:
>>>> >> ````
>>>> >> template<typename...T>
>>>> >> void func(T&&...args){
>>>> >> }
>>>> >> int main(){
>>>> >> int a{};
>>>> >> char b{};
>>>> >> func(a,b);
>>>> >> }
>>>> >> ````
>>>> >> we know the deduced template argument are `int&` and `char&`. That
>>>> is, the function type is void(int&,char&). we know the reference collapsing
>>>> rules is applying for this case. However, I have to say that the identifier
>>>> `T` is neither a typedef-name nor decltype-specifier, because of the
>>>> following rule:
>>>> >>
>>>> >> A type-parameter whose identifier does not follow an ellipsis
>>>> defines its identifier to be a typedef-name (if declared without template)
>>>> or template-name (if declared with template) in the scope of the template
>>>> declaration.
>>>> >>
>>>> >> So, these rules are contradicting with each others. How to interpret
>>>> this? Is it a defect in the standard?
>>>> >> --
>>>> >> Std-Discussion mailing list
>>>> >> Std-Discussion_at_[hidden]
>>>> >> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>>>>
>>>

Received on 2020-09-07 09:27:37