c++referencelanguage-lawyerdirect-initialization

In which case(s) user-defined conversions are not considered during reference initialization?


Consider a case where we've reached into bullet [dcl.init.ref]/(5.4.1) during reference binding:

(5.4.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.

How, during direct initialization of a reference, user-defined conversions are not considered?

If possible, support the answer with an example in which this rule is applied.


Solution

  • As far as I know, the bolded sentence is redundant. It may have been added out of an abundance of caution.

    The predecessor of the quoted paragraph, in C++11, actually required a temporary of type "cv1 T1" to be copy-initialized from the initializer expression, whereupon the reference to type "cv1 T1" (that we are trying to initialize) would be bound to that reference. Simple, right? There is no question of how such binding is to occur. The reference simply becomes an alias for that temporary object and we are done.

    When CWG1604 was resolved just prior to the publication of C++14, the wording was changed so that, instead of this paragraph requiring the copy-initialization of a temporary of type "cv1 T1", we instead do the following:

    Because x might not be of type cv1 T1, a second run through the reference initialization algorithm is required in order to determine how to direct-initialize r from x. The old wording didn't require a second run. The drafters may have been concerned that there would be an edge case where the new wording would require consideration of user-defined conversions in the second run. But I think we can prove that there are no such cases.

    We can consider all possible ways in which the copy-initialization of a hypothetical cv1 T1 object could be done by consulting [dcl.init.general]/17, which governs copy-initializations:

    Note that all references above are to the C++20 standard.

    I suspect that the resolution to this issue might have been drafted in a hurry since it was not fixed until after the committee draft had been sent out and it appears that the Canadian NB asked the committee to fix it. The drafters might have decided to just insert the clause about not using user-defined conversions rather than doing an in-depth study to determine whether it is needed. In addition, it seems "cleaner" to avoid even conceptual recursion (i.e. instead of re-running the reference initialization algorithm with different inputs on which the logic of the algorithm guarantees that a further recursive call won't occur, we run a more limited algorithm).