Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload-Resolution: Is a direct conversion operator preferred (as a consequence of copy-elision)?

Given

struct E
{
};

struct P
{
    explicit P(E) {}
};

struct L
{
    operator E() {return {};}
    operator P() {return P{E{}};}
};

According to the C++17 language standard, should the expression P{L{}} compile?

Different compilers produce different results:

  • gcc (trunk): ok
  • gcc 8.3: error (overload ambiguous)
  • gcc 7.4: ok
  • clang (trunk): ok
  • clang 8.0.0: ok
  • clang 7.0.0: ok
  • msvc v19.20: error (overload ambiguous)
  • icc 19.0.1: error (more than one constructor instance matches)
like image 788
precarious Avatar asked Apr 30 '19 18:04

precarious


1 Answers

I think the correct behavior per the standard is to be ambiguous.

[dcl.init]/17.1:

If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized.

[dcl.init.list]/3.6:

Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution ([over.match], [over.match.list]). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

And [over.match.list] just talks about picking a constructor. We have two viable options: P(E) by way of L{}.operator E() and P(P&&) (the implicit move constructor) by way of L{}.operator P(). None is better the other.


However, this is very reminiscent of CWG 2327:

struct Cat {};
struct Dog { operator Cat(); };

Dog d;
Cat c(d);

Which as the issue indicates currently invokes Cat(Cat&&) instead of just d.operator Cat() and suggests that we should actually consider the conversion functions as well. But it's still an open issue. I'm not sure what gcc or clang did in response to this issue (or in response to similar examples that got brought up first), but based on your results I suspect that they decide that the direct conversion function L{}.operator P() is a better match and just do that.

like image 133
Barry Avatar answered Nov 10 '22 17:11

Barry