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

c++ - Possible C++17 type conversion change and MSVC vs GCCclang, who's right? - Stack Overflow

programmeradmin2浏览0评论
struct B;

struct A {
    operator B() const;
};

struct B {};

void f() {
    (void)B(A{});
}

This works with GCC, clang and MSVC as expected.

If you add another conversion and a corresponding constructor, MSVC stops working:

struct B;

struct A {
    operator B() const;
    operator int() const;
};

struct B {
    B(int);
};

void f() {
    (void)B(A{});
}

MSVC error:

<source>(13): error C2440: '<function-style-cast>': cannot convert from 'A' to 'B'
<source>(13): note: 'B::B': ambiguous call to overloaded function
<source>(10): note: could be 'B::B(B &&)'
<source>(10): note: or       'B::B(const B &)'
<source>(9): note: or       'B::B(int)'
<source>(13): note: while trying to match the argument list '(A)'

Who is right here: GCC and clang or MSVC?

I came across this in a C++20 context. For C++17/20/23 its the same, but for C++11/14 I get a similar error with GCC and a less similar error with clang. So it might be a rule change in C++17.

C++14 GCC error:

<source>:13:16: error: call of overloaded 'B(A)' is ambiguous
   13 |     (void)B(A{});
      |                ^
<source>:9:5: note: candidate: 'B::B(int)'
    9 |     B(int);
      |     ^
<source>:8:8: note: candidate: 'constexpr B::B(const B&)'
    8 | struct B {
      |        ^
<source>:8:8: note: candidate: 'constexpr B::B(B&&)'

C++14 clang error:

<source>:13:11: error: ambiguous conversion for functional-style cast from 'A' to 'B'
   13 |     (void)B(A{});
      |           ^~~~~~
<source>:8:8: note: candidate constructor (the implicit copy constructor)
    8 | struct B {
      |        ^
<source>:8:8: note: candidate constructor (the implicit move constructor)
<source>:9:5: note: candidate constructor
    9 |     B(int);
      |     ^
struct B;

struct A {
    operator B() const;
};

struct B {};

void f() {
    (void)B(A{});
}

This works with GCC, clang and MSVC as expected.

If you add another conversion and a corresponding constructor, MSVC stops working:

struct B;

struct A {
    operator B() const;
    operator int() const;
};

struct B {
    B(int);
};

void f() {
    (void)B(A{});
}

https://godbolt./z/G7fE998T6

MSVC error:

<source>(13): error C2440: '<function-style-cast>': cannot convert from 'A' to 'B'
<source>(13): note: 'B::B': ambiguous call to overloaded function
<source>(10): note: could be 'B::B(B &&)'
<source>(10): note: or       'B::B(const B &)'
<source>(9): note: or       'B::B(int)'
<source>(13): note: while trying to match the argument list '(A)'

Who is right here: GCC and clang or MSVC?

I came across this in a C++20 context. For C++17/20/23 its the same, but for C++11/14 I get a similar error with GCC and a less similar error with clang. So it might be a rule change in C++17.

C++14 GCC error:

<source>:13:16: error: call of overloaded 'B(A)' is ambiguous
   13 |     (void)B(A{});
      |                ^
<source>:9:5: note: candidate: 'B::B(int)'
    9 |     B(int);
      |     ^
<source>:8:8: note: candidate: 'constexpr B::B(const B&)'
    8 | struct B {
      |        ^
<source>:8:8: note: candidate: 'constexpr B::B(B&&)'

C++14 clang error:

<source>:13:11: error: ambiguous conversion for functional-style cast from 'A' to 'B'
   13 |     (void)B(A{});
      |           ^~~~~~
<source>:8:8: note: candidate constructor (the implicit copy constructor)
    8 | struct B {
      |        ^
<source>:8:8: note: candidate constructor (the implicit move constructor)
<source>:9:5: note: candidate constructor
    9 |     B(int);
      |     ^
Share Improve this question asked Mar 31 at 15:58 Benjamin BuchBenjamin Buch 6,1658 gold badges35 silver badges63 bronze badges 9
  • The solution is to mark the constructor or conversion operator explicit (or both) – Alan Birtles Commented Mar 31 at 16:23
  • 2 I'm almost certain that this has to do with the mandatory copy elision that was added in C++17 but don't quite know standardeese enough to formulate an answer :-) – Ted Lyngmo Commented Mar 31 at 16:43
  • Clang uses operator B according to cppinsight Demo – Jarod42 Commented Mar 31 at 16:51
  • 2 @PeteBecker: that avoid extra warning, you might use also[[maybe_unused]]B b(A{}); – Jarod42 Commented Mar 31 at 17:54
  • 1 "Noise" in code to avoid noise in warning... – Jarod42 Commented Mar 31 at 18:00
 |  Show 4 more comments

1 Answer 1

Reset to default 3

This divergence has to do with CWG2327: when direct-initializing an object by calling a conversion function, if we want to be able to elide the temporary that would normally be produced by that conversion function, then some changes are needed to the current language specification. P2828 discusses the approaches taken by the existing implementations at the time. On one side you have Clang and GCC, which take more "aggressive" approaches, and on the other side EDG and MSVC, which are much more "conservative".

Essentially, EDG and MSVC leave the current overload resolution rules intact. In the OP's first example

struct B;

struct A {
    operator B() const;
};

struct B {};

void f() {
    (void)B(A{});
}

current overload resolution rules state that B's move constructor is called to initialize the B object, and the rvalue reference parameter binds to the B prvalue returned from A::operator B. In order to elide the temporary B object, EDG and MSVC have a rule where the call to the move constructor is simply skipped. But when the constructor B(int) is added, we have a problem because now it's ambiguous whether B(int) or B(B&&) should win overload resolution.

In Clang and GCC, B(B&&) outranks B(int). I won't get too deeply into why. Also, since this paper was published, I believe GCC at least has adjusted its approach slightly (not sure about Clang). In other words the paper is outdated by now.

Some background about this paper: it basically proposed the EDG behaviour and almost made it into the standard, but at the last moment CWG decided to consider a different direction. However, nailing down the specifics has proven more difficult than originally thought. That's why a fix for this issue hasn't been applied to the standard yet.

But everyone more or less agreed that we don't want to change the outcome of overload resolution any more than necessary in order to get copy elision in the cases where we don't currently have copy elision. Meaning that your example that is reported as ambiguous by MSVC will probably still be ambiguous after whatever changes are needed to fix CWG2327 are applied to the standard.

发布评论

评论列表(0)

  1. 暂无评论