1

This question can be considered probably theoretical as I don't see why I would have such design in real code. I've got two classes with some conversion operators:

class B;
class A {
 public:
 A() = default;
 A(B const &);
};
class B {
 public:
 B() = default;
 operator A();
};
A::A(B const &) {};
B::operator A() { return A{}; };
int main() {
 [[maybe_unused]] A ad{B{}}; // direct init
 [[maybe_unused]] A ac = B{}; // copy init
}

LIVE

With gcc only and with copy initialization only I've got this warning:

warning: choosing 'B::operator A()' over 'A::A(const B&)' [-Wconversion]
warning: for conversion from 'B' to 'A' [-Wconversion]
note: because conversion sequence for the argument is better

Not being well-versed in the rules of conversion, I do not understand if this warning is relevant? If not, how can it be silenced (it can be painful with -Werror)?

NB Adding a bit of traces, I observed different behaviors according to compilers and the initialization nature. Are conversion sequences implementation-dependent to some extent?

asked Feb 7, 2025 at 14:17
9
  • This warning is relevant as the compiler cannot necessarily know if A::A(B const &) has an equivalent implementation to B::operator A() at the call site Commented Feb 7, 2025 at 14:24
  • 4
    It's a warning because looking at A you would assume that its converting constructor would be used, but it isn't. In most cases, this is a surprise. Implicit conversions are Evil. The best remedy is to avoid them. Commented Feb 7, 2025 at 14:25
  • 1
    The warning is informing you that as the author of that constructor, your apparent intention to use it is problematic because the conversion operator takes precedence. You can choose to ignore the warning but it means code you've written that will not be used. Commented Feb 7, 2025 at 14:25
  • 2
    It's not ambiguous though; that would be an error, not a warning. Commented Feb 7, 2025 at 14:27
  • 2
    There shouldn't be any difference between compilers, one of these outputs is wrong. Which one, I have no idea, initialisation in C++ standard is a rabbit hole I'd rather not enter. Commented Feb 7, 2025 at 14:50

1 Answer 1

3

(Not a 100% confident answer, but I'm not sure one is possible.)

Part of the complexity here is that your operator A() is not a const member function. So we have a choice between a conversion function taking a non-const this, and a converting constructor taking a const this (which is a slightly worse match in this case because your input B prvalue is non-const).

Godbolt:

struct A {
 A(const B&) { puts("A(const B&)"); }
};
struct B {
 operator A() { puts("B::operator A()"); return A(); }
};
A f(B b) {
 return A(b);
 // Clang in >= C++17 mode: B::operator A()
 // Everyone else: A(const B&)
}
A g(B b) {
 return b;
 // MSVC in <= C++17 mode: A(const B&)
 // Everyone else: B::operator A()
}

If you make it B::operator A() const, then most compilers call return b ambiguous (Godbolt), while still permitting return A(b).

struct A {
 A(const B&) { puts("A(const B&)"); }
};
struct B {
 operator A() const { puts("B::operator A()"); return A(); }
};
A f(B b) {
 return A(b);
 // Everyone: A(const B&)
}
A g(B b) {
 return b;
 // GCC: A(const B&)
 // MSVC in <= C++17 mode: A(const B&)
 // Everyone else: Ambiguous
}

This is basically the topic of CWG 2327; see also Preference of conversion operator over copy constructor changes from C++14 to C++17? and Call to conversion operator instead of converting constructor in c++17 during overload resolution .

As for why Clang prefers the conversion function over the converting constructor, I'm not 100% sure but it seems like Clang bug #89501 could be related. It sounds like the current behavior just kinda happened, not necessarily by design; but they're not planning to touch it until CWG 2327 gets resolved.

answered Feb 8, 2025 at 0:16
Sign up to request clarification or add additional context in comments.

1 Comment

Having operator A() being non const was an oversight on my part. But your answer covers thoroughly both case. Thanks a lot!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.