3
#include <iostream>
struct Data{};
struct Test{
    Test() = default;
    Test(Data){}
};
int main(){
  Data d;
  Test const& rf = d;
}

Consider the above code, The standard says:

Otherwise:

  • 5.2.2.1 If T1 or T2 is a class type and T1 is not reference-related to T2, 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.
  • 5.2.2.2 Otherwise, the initializer expression is implicitly converted to a prvalue of type “cv1 T1”. The temporary materialization conversion is applied and the reference is bound to the result.

So, which bullet does the above case obey? The initializer expression is converted to type Test through converting constructor Test::Test(Data) rather than conversion function. However note the emphasized part in 5.2.2.1, It says that the result of the call to the conversion function is then used to direct-initialize the reference. In my example, the called function was converting constructor, hence, the resulting was resulted from converting constructor.

Issue 1:

which bullet covers my example? 5.2.2.1 or 5.2.2.2?


5.2.1.2 has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref]),

Consider the bullet 5.2.1.2, It has already covered the case which type T2 is a class type, and it can be converted to cv3 T3 through conversion function.

Issue 2:

So, Is it redundant that 5.2.2.1 covers T2 is a class type, and it can be converted to destination type through conversion function, Such case has already covered in 5.2.1.2?

xmh0511
  • 7,010
  • 1
  • 9
  • 36

1 Answers1

2

Issue 1

[dcl.init.ref]/5.2.2.1 applies here. It is the paragraph covering user-defined conversions. One of the acceptable conversion mechanisms is 16.3.1.4 [over.match.copy] which can use converting constructors on T1. The value is converted using this constructor and the resulting temporary is bound to the reference.

[dcl.init.ref]/5.2.2.2 applies to cases of implicit conversion not including user-defined conversion, such as widening numeric conversions.

Issue 2

From [dcl.init.ref]:

(5.2.1) If the initializer expression

...

(5.2.1.2) has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see 16.3.1.6),

Jumping to 16.3.1.6 [over.match.ref], there is a lot of prose here, but this is the only relevant part:

(1) ... Assuming that “reference to cv1 T” is the type of the reference being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

(1.1) The conversion functions of S and its base classes are considered. ...

The rest of the section details which conversion functions of S are eligible to be used, but that's unimportant to the situation in the example code. [over.match.ref] considers only conversion operators on the type of the value being used to initialize the reference, which would not be the case here -- Data has no implicit conversion operators. This section makes no reference to converting constructors of T.

Therefore [over.match.ref] and by extension [dcl.init.ref]/5.2.1.2 do not apply to this case.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • For issue 1, I think it should be a defect because `The result of the call to the conversion function` sounds like it does not intent to cover `the result of the call to the converting constructor`. For issue 2, my meaning is `(5.2.1.2)` already cover the situation that the conversion functions of `S` are used to convert the initializer to destination type, why `5.2.2.1` cover such case repeatedly? In other words, if exists a conversion function in type `S`(the type of initializer expression), it would be covered by `(5.2.1.2)` and never step in `5.2.2.1` – xmh0511 Aug 18 '20 at 07:02
  • @jackX The phrase "conversion function" in 5.2.2.1 is almost certainly intended to mean "whatever user-defined conversion mechanism is selected." The intent is clear in context. The wording could be improved but I'd not call it a defect. Regarding 5.2.1.2, I'm confused about what you're saying. It sounds like you're describing a scenario that your example code doesn't even demonstrate? – cdhowie Aug 18 '20 at 07:14
  • I have reread [over.match.ref] and [over.match.conv] carefully. I find the difference between `(5.2.1.2)` and `(5.2.2.1)` for selecting conversion functions. [Demonstrate](https://godbolt.org/z/j7YhWj). – xmh0511 Aug 18 '20 at 07:34
  • The definition of the `conversion function` is [here](https://timsong-cpp.github.io/cppwp/n4659/class.conv.fct#def:conversion_function), I think such wording could make it unclear. – xmh0511 Aug 18 '20 at 07:37
  • @jackX Right, the difference you demonstrated is my understanding as well. Using a converting constructor on the underlying type of the reference (as in the code in your question) is a case of 5.2.2.1. – cdhowie Aug 18 '20 at 07:49
  • But the second phase in [over.match.copy] for `5.2.2.1` seems to be overlapped with [over.match.ref] for `(5.2.1.2)`(which conversion functions are eligible to be used). In other words, if we analyze `(5.2.1.2)` and `(5.2.2.1)` separately. They all cover such [case](https://godbolt.org/z/73Gzjj), however `(5.2.1.2)` is before `(5.2.2.1)`, So such case is covered by `(5.2.1.2)`, `(5.2.2.1)` never step in. – xmh0511 Aug 18 '20 at 08:06
  • @jackX That is my understanding as well. 5.2.2.1 could handle it alone, but by exclusion it never will since 5.2.1.2 does first. – cdhowie Aug 18 '20 at 08:20