2

Lets consider simple reduced example

struct Foo {};
struct Bar : Foo {};
struct Baz : Foo {};

struct X {
  operator Bar() { std::cout << "Bar\n"; return {}; }
  operator Baz() const { std::cout << "Baz\n"; return {}; }
};

void foo(const Foo &f) {}

int main() {
  foo(X{});
}

Here both GCC and clang are printing "Bar", see godbolt example

I am struggling to understand why.

There is no problem to call const-annotated method in rvalue object, see this example

So lets build ICS:

X{} -> user conversion -> Bar -> const Foo&
X{} -> user conversion -> Buz -> const Foo&

Both ICS have 1 user conversion and equivalent standard conversion tail. This shall be ambiguity by [over.ics.rank] C++20.

Can someone help to motivate what happens? I don't believe both mainline compilers agree and wrong here.

Konstantin Vladimirov
  • 6,791
  • 1
  • 27
  • 36
  • rvalue objects that aren't fundamentals like int, float or pointers, aren't necessarily const. And in your example it isn't const. – doug May 06 '23 at 19:42
  • 1
    @Konstantin Vladimirov X{} is a non-constant object. So the non-constant method is selected. – Vlad from Moscow May 06 '23 at 19:43
  • `using const_X = X const; foo(const_X{});` does the other one. – Eljay May 06 '23 at 19:43
  • @Konstantin Vladimirov By the way do you visit rsdn? – Vlad from Moscow May 06 '23 at 19:45
  • Intuition behind non-const method is clear to me as well, thanks. But as I am reading standard, seems it works only if we have two identical ICS one passing through const one not. – Konstantin Vladimirov May 06 '23 at 19:48
  • Please leave only Baz (comment out Bar operator) and you will see everything works. Both ICS are viable and allowed. – Konstantin Vladimirov May 06 '23 at 19:50
  • They are both allowed, but `Bar` is favoured (in your example). Something similar here: https://stackoverflow.com/questions/76167190/how-to-do-non-const-member-function-used-on-objects-left-side-and-const-member/76167264 – Paul Sanders May 06 '23 at 19:50
  • @KonstantinVladimirov A constant method may be called for a non-constant object. But a non-constant method may not be called for a constant object. – Vlad from Moscow May 06 '23 at 19:54
  • You can try `operator Baz() && { std::cout << "Bada boom\n"; return {}; }` – 273K May 06 '23 at 20:14
  • 1
    Does this answer your question? [Overload resolution C++ for const member functions](https://stackoverflow.com/questions/21469135/overload-resolution-c-for-const-member-functions) – The Dreams Wind May 06 '23 at 20:48
  • 1
    Better dup candidate https://stackoverflow.com/questions/11555950/distinguishing-between-user-defined-conversion-sequences-by-the-initial-standard – Language Lawyer May 06 '23 at 21:03
  • @TheDreamsWind I think it is obvious this is different case. There is no overload resolution between const and non-const foo in my question I have only one function foo with const argument. – Konstantin Vladimirov May 06 '23 at 21:41
  • @LanguageLawyer thanks it is really close and answer here is also great! – Konstantin Vladimirov May 06 '23 at 21:41
  • @KonstantinVladimirov nevertheless the same rules apply to your question. You even confirmed it yourself by accepting the given answer – The Dreams Wind May 06 '23 at 21:43

1 Answers1

1

foo(X{}) attempts to initialize a parameter of type const Foo& with a prvalue of type X.

This falls under [dcl.init.ref]/5.3.2, which directs us to [over.match.ref] to select a conversion function through overload resolution.

Both operator Bar() and operator Baz() const are candidates, but the former is a better match because its object parameter is not const ([over.ics.rank]/3.2.6).

So, the (only) conversion sequence from X to const Foo& goes through Bar. And we never rank it against another conversion sequence because there is only one foo anyway.


An ambiguity would arise in a situation like this:

void foo(Bar);
void foo(Baz);
foo(X());

Now the call is ambiguous because the ICS from X to Bar is indistinguishable from X -> Baz, despite its first standard conversion sequence being better.

duck
  • 1,455
  • 2
  • 8