C++ Logo

std-discussion

Advanced search

Re: The issue about reference collapsing for function parameter pack

From: jim x <xmh970252187_at_[hidden]>
Date: Tue, 8 Sep 2020 10:05:17 +0800
Andrew Schepler <aschepler_at_[hidden]> 于2020年9月7日周一 下午10:24写道:

> 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.
>>
>>
I think, because a template parameter pack(type-parameter) is a collection
of various types, and since a typedef-name can only denote one type, so
[temp.param#3] should cover such a case and point out such a pack name is
not to be considered as a typedef-name. Maybe as you said,
[temp.variadic#7.1]
<https://timsong-cpp.github.io/cppwp/n4659/temp.variadic#7.1> describes how
to instantiate the pattern when the pack is a template parameter pack.
IIUC, for a function parameter pack declaration, that is `T&&...args`, the
pattern should be `T&& args`, where the pack expansion parameter `T` is
replaced by these elements in the collection of pack expansion parameters,
these elements as said in [temp.variadic#7.1], which are *template
parameter* of the corresponding kind, maybe the corresponding template
parameter declaration will be invented for these template parameter which
will be used to replace the pack expansion parameter(which would be like
`template<typename template-parameter-1, typename template-parameter-2,...,
typename template-parameter-n>`), so these invented template parameter
declarations satisfy the form says in [temp.param#3], which are
typedef-name. In addition, although the standard does not give a definition
for `pack expansion parameter`, we infer that such a stuff is the parameter
appears in a pattern, that is the pack 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 21:09:01