Skip to content

CWG2985 [dcl.init.ref] Direct-initialization of a reference after conversion #596

@ranaanoop

Description

@ranaanoop

Full name of submitter: Anoop Rana

Reference (section label): [dcl.init.ref]

Link to reflector thread (if any): https://stackoverflow.com/questions/78867403/reference-initialization-using-list-initialization-accepted-by-gcc-and-msvc-but

Issue description:

There are two main issues described here. First is that dcl.init.ref#5.4.1.sentence-2 doesn't specify if the reference is direct initialized using () or using {}. This matters because X&& ref{z}; form is accepted by three compilers while X&& r = z; is rejected by all. The second issue is that we should make X&& r =z; and X&&{z}; be more consistent and ill-formed.
So to make the issue clearer, consider the following example: Demo


struct X
{
    public:
    X(){}
};
struct Y : X 
{
    
};
struct Z
{
    public:
        operator const Y () const
        {
            return {};
        }
};
int main()
{
    Z z;
    // X&& r = z; // #1: All four rejects this
    X&& ref{z}; //#2:   Clang: Nope, gcc: Ok, msvc: Ok, Edg: Ok
}

First we see how/why #1 is almost ill-formed as per the current wording. I say "almost" because [dcl.init.ref#5.4.1] doesn't specify if () or {} form is used for the direct initialization. And this might make a difference as already seen from X&& ref{z}; which is accepted by 3 compilers but not clang.

So to understand #1 we go to dcl.init.ref:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • [...]
  • [...]
  • [...]
  • Otherwise, T1 shall not be reference-related to T2.
    • If T1 or T2 is a class type, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ([dcl.init], [over.match.copy], [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed.
      The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference.
      For this direct-initialization, user-defined conversions are not considered.

This means that the result of the conversion function which will be of type const Y will be used to direct initialize r.

So the initialization process will be repeated again. This time we first go to direct initialization:

If the destination type is a reference type, see [dcl.init.ref].

So now come to dcl.init.ref and see that none of the bullet points is applicable for initialization of the reference r from const Y.

In particular, 5.1 is not applicable because the reference is not lvalue reference.

Similarly 5.2 is not applicable.

5.3 is also not applicable.

5.4 is not applicable because const Y is reference related to X as X is a base.

So none of the bullet points are applicable for the direct initialization of r by const Y and so X&& r = z; is ill-formed.

The important thing here is that here we didn't go to dcl.init#general-16.1 but instead to dcl.init#general-16.2. But the dcl.init.ref#5.4.1.sentence-2 currently doesn't specify that {} form of direct initialization is not used.

We can solve this first issue by changing "direct initialization" to "direct-non-list-initialization" or directly saying () form is used instead of {}.

Additionally, there might also be another issue(that of consistency) between not allowing X&& r = z; but allowing X&& ref{z};. That should also be fixed(if any) by additional wording changes(apart from what is suggested here).

Suggested Resolution

Change dcl.init.ref#5.4.1.sentence-2 to as highlighted below in bold:

The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference using direct-non-list-initialization. For this direct-initialization, user-defined conversions are not considered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions