The following code compiles fine with both g++ 9.1 and clang 8.0.0 (compilation flags are -std=c++17 -Wall -Wextra -Werror -pedantic-errors
), but not with MSVC 19.22 (compilation flags are /std:c++17 /permissive-
):
struct X{};
struct Bar
{
Bar() = default;
Bar(X){}
};
struct Foo
{
operator X() const
{
return X{};
}
operator Bar() const
{
return Bar{};
}
};
int main()
{
Foo foo;
[[maybe_unused]]Bar b1 = foo; // OK
[[maybe_unused]]Bar b2(foo); // failed
}
MSVC compilation errors:
<source>(27): error C2668: 'Bar::Bar': ambiguous call to overloaded function
<source>(8): note: could be 'Bar::Bar(Bar &&)'
<source>(7): note: or 'Bar::Bar(X)'
<source>(27): note: while trying to match the argument list '(Foo)'
Is it a bug in MSVC?
A conversion operator, in C#, is an operator that is used to declare a conversion on a user-defined type so that an object of that type can be converted to or from another user-defined type or basic type. The two different types of user-defined conversions include implicit and explicit conversions.
Conversion Operators in C++ C++ supports object oriented design. So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes. To make this conversion we can use conversion operator.
I think this is basically a manifestation of CWG 2327, which dealt with this example:
struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);
The crux of the issue was that we don't allow for guaranteed copy elision in this case - because we go through Cat
's move constructor rather than just initializing via Dog::operator Cat()
directly.
And it seems like gcc and clang both already implement the intent of the issue - which is to do overload resolution on both the constructors and conversion functions at the same time.
In your example:
Bar b2(foo);
Per the letter of the standard, we consider constructors (and only constructors) - which are Bar(X)
, Bar(Bar const&)
, and Bar(Bar&&)
. All three of those are viable, the first by way of Foo::operator X() const
and the second and third by way of Foo::operator Bar() const
. We can prefer Bar(Bar&&)
to Bar(Bar const&)
but we have way of disambiguating between Bar(X)
and Bar(Bar&&)
. MSVC is following the standard in correctly rejecting this initialization. It is not a bug.
But the spirit of CWG 2327 is that this should invoke Foo::operator Bar() const
directly, which is what gcc and clang do. It is hard to say that this is a bug on their side either since that's probably the behavior we actually want to happen, and will likely be the way it is specified at some point in the future.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With