14

Consider the following:

struct X {
    template <class T> operator T();  // #1
    template <class T> operator T&(); // #2
};

int        a = X{}; // error: ambiguous
int&       b = X{}; // calls #2
int const& c = X{}; // calls #2

The situation for b is straightforward, #2 is the only viable candidate. What is the rule that indicates that #2 is preferred to #1 for initialization of int const&, but the two are ambiguous for initialization of int?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    [\[over.match.conv\]](http://eel.is/c++draft/over.match.conv) and [\[over.match.ref\]](http://eel.is/c++draft/over.match.ref) seem like a good starting point. – jaggedSpire Sep 01 '16 at 18:03
  • @jaggedSpire Those sections indicate what the candidate functions are. Not how to prefer (or not) one or the other. – Barry Sep 01 '16 at 18:08
  • 2
    the templating is not essential here, plain `operator int` and `operator int&` also are ambiguous – TemplateRex Sep 01 '16 at 18:11
  • "Those non-explicit conversion functions that ... yield type “lvalue reference to cv2 T2” (when initializing an lvalue reference ...) where “cv1 T” is reference-compatible ([dcl.init.ref]) with “cv2 T2”, are candidate functions" seems to indicate to me a restriction for initializing lvalue types that would disqualify #1 for `int const&` – jaggedSpire Sep 01 '16 at 18:16
  • @TemplateRex I guess that adds another followup question about `operator T&` and `operator T const&`, which is nonambiguous is one case where the non-template versions are ambiguous. – Barry Sep 01 '16 at 18:25
  • @TemplateRex Yes, but what's the deal with `c`? – Barry Sep 01 '16 at 18:26
  • 1
    @TemplateRex I know it's unambiguous. The question is why is `a` ambiguous but not `c`. – Barry Sep 01 '16 at 18:36

1 Answers1

3

When deciding how to initialize a reference given its initializer, first, direct binding is tried. [dcl.init.ref]/(5.1.2):

If the reference is an lvalue reference and the initializer expression […] 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 lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) and choosing the best one through overload resolution (13.3)), then the reference is bound […] to the lvalue result of the conversion […].

The wording that governs the candidate selection for this process (13.3.1.6, as mentioned above) excludes the first conversion function:

The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type “lvalue reference to cv2 T2(when initializing an lvalue reference or an rvalue reference to function) […], where “cv1 T” is reference-compatible (8.6.3) with “cv2 T2”, are candidate functions. For direct-initialization, […].

Clearly, this exclusion is specific to the reference initialization semantics, so the first case is ambiguous still.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • But you omitted the part of the section that considers conversion functions that yield *cv2* `T2`. What happened to that other candidate function? – Barry Sep 01 '16 at 18:29
  • @Barry doesn't that only apply to "initializing an rvalue reference or an lvalue reference to function", or am I missing your meaning? – jaggedSpire Sep 01 '16 at 18:32
  • @jaggedSpire I was gonna say, barry confused me for a second. If a conversion function returns a prvalue, how can an lvalue reference bind directly to it? That would kinda miss the point of that section. – Columbo Sep 01 '16 at 18:33
  • Yeah that's what I was missing. So the reason that `operator T()` is a candidate isn't because 13.3.1.6 says it is, but because we fallback to 8.6.3/5.2.2.1 and 13.3.1.5 finds it. Right? – Barry Sep 01 '16 at 18:45
  • @Barry A candidate in what situation? – Columbo Sep 01 '16 at 18:47
  • @Columbo In `int const& c = X{}`. Assuming `operator T&()` didn't exist. Perhaps candidate is the wrong word, since the two aren't considered at the same time in the way overload resolution typically works. – Barry Sep 01 '16 at 18:56