[cfe-dev] Strange phenomenon with conversion operator and call of overloaded method

Johannes Schaub (litb) schaub.johannes at googlemail.com
Tue May 31 10:19:37 PDT 2011


Jonathan Sauer wrote:

> Hello,
> 
> while fooling around with conversion operators, I stumbled upon the
> following:
> 
> struct Foo { };     // (A)
> //using Foo = int;    // (B)
> 
> template <typename T>
> class A {
>     public:
>         operator const T&() const { return m_t; }
>         
>     private:
>         T   m_t;
> };
> 
> static void bar(Foo&&) { }  // (C)
> static void bar(const Foo&) { }
> 
> int main(int, char**)
> {
>     A<Foo>              a;
>     bar(a);
>     bar((const Foo&) a);  // (D)
> }
> 
> 
> Compiling this program with clang r132331 in C++0x mode results in the
> following error message:
> 
> clang.cpp:19:9: error: no viable conversion from 'A<Foo>' to 'Foo'
>     bar(a);
>         ^
> clang.cpp:1:8: note: candidate constructor (the implicit copy constructor)
> not viable: no known
>       conversion from 'A<Foo>' to 'const Foo &' for 1st argument
> struct Foo { };     // (A)
>        ^
> clang.cpp:7:9: note: candidate function
>         operator const T&() const { return m_t; }
>         ^
> clang.cpp:13:22: note: passing argument to parameter here
> static void bar(Foo&&) { }  // (C)
> 
> 
> First of all, clang does not tell why the noted candidate function is not
> viable. Second of all, for some reason clang does not consider the
> overload bar(const Foo&), even though it would match.
> 

You can interpret the spec so that it yields exactly clang's behavior, I 
think. The spec says that for a reference binding of "Foo&&" to "A<Foo>", 
the following conversion functions are candidates (S is the initializer, 
A<Foo>, and T is the type "Foo"):

"Those that are not hidden within S and yield type “lvalue reference to cv2 
T2” (when 8.5.3 requires an lvalue result) or “cv2 T2” or “rvalue reference 
to cv2 T2” (when 8.5.3 requires an rvalue result), where “cv1 T” is 
reference-compatible (8.5.3) with “cv2 T2”, are candidate functions."

This reads to me that "operator const T&" is *not* a candidate here, because 
"const T&" is an lvalue reference. However, the example in 8.5.3 indicates 
that the intent seems that we *do* consider "operator const T&" as a 
candidate. Consider the similar example it gives at 8.5.3:

struct X {
operator B();
operator int&();
} x;

int &&rri2 = X(); // ill-formed: lvalue-to-rvalue conversion on result of 
operator int&

So assuming that clang follows this intent, we would have it choose 
"operator const T&" as a candidate conversion function, and (because we are 
initializing an rvalue reference), will do an lvalue to rvalue conversion on 
the lvalue it yields (according to 3.10p2).

So for the first "bar", you bind an rvalue reference to an rvalue. For the 
second bar, the conversion sequence would bind an lvalue reference to an 
lvalue. These two user defined conversion sequences use the same conversion 
function, and hance can be compared as follows:

- The first bar's parameter conversion sequence wins, because the second 
conversion function binds an rvalue reference to an rvalue, while the 
competing conversion sequence binds an lvalue reference to an lvalue (c.f. 
second toplevel bullet of 13.3.3.2p3 and one of the subbullets of the former 
toplevel bullets).

Clang thus selects the first bar, and then the initialization of the 
parameter fails, because of 8.5.3 saying that when an lvalue to rvalue 
conversion happens, the program is ill-formed. 





More information about the cfe-dev mailing list