最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Ambiguous conversion warning: should I care? - Stack Overflow

programmeradmin0浏览0评论

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?

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?

Share Improve this question asked Feb 7 at 14:17 OerstedOersted 2,5946 silver badges25 bronze badges 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 – Mestkon Commented Feb 7 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. – molbdnilo Commented Feb 7 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. – Patrick Roberts Commented Feb 7 at 14:25
  • 2 It's not ambiguous though; that would be an error, not a warning. – molbdnilo Commented Feb 7 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. – Yksisarvinen Commented Feb 7 at 14:50
 |  Show 4 more comments

1 Answer 1

Reset to default 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.

发布评论

评论列表(0)

  1. 暂无评论