Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does overload resolution prefer a move constructor over a non-special constructor when equally good conversion functions are available?

The following program:

#include <iostream>

struct A 
{ 
  A() { std::cout << "A()\n"; }
  A(int) { std::cout << "A(int)\n"; }
};

struct C {
  operator int() {
    std::cout << "operator int\n";
    return 42;
  }
  operator A() {
    std::cout << "operator A\n";
    return A();
  }
};

int main() {
    auto a = A{C{}};
};

compiles and, when run, prints:

operator A
A()

See Godbolt link.

This shows that the move constructor of A was selected to perform the initialization, with C::operator A() being invoked to convert C into the type that the move constructor expects.

Alternatively, the A::A(int) constructor could have been selected, with C::operator int() being invoked. But A::A(int) loses overload resolution, apparently.

Why does this happen? I am not able to see any rule in the standard that explains why the move constructor of A wins the overload resolution.

(This question is inspired by this answer; I could not understand why the code in that answer works.)

like image 466
Brian Bi Avatar asked Feb 21 '21 22:02

Brian Bi


1 Answers

It appears that both GCC and Clang implemented a fix for CWG2327 by directly considering conversion functions to cv T as candidates when initializing an object of type cv2 T, in addition to constructors.

When C::operator A() is considered as a candidate itself, it is an exact match, and therefore prevails over the constructor call which requires a user-defined conversion.

That issue is still in drafting status, so you won't be finding this in the wording.

Notably both reject with -std=c++14.

like image 84
T.C. Avatar answered Oct 20 '22 04:10

T.C.