Con*_*tor 6 c++ initialization conversion-operator language-lawyer c++17
下面的代码编译罚款既克++ 9.1和铛8.0.0(编译标志是-std=c++17 -Wall -Wextra -Werror -pedantic-errors),但不与MSVC 19.22(编译标志是/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
}
Run Code Online (Sandbox Code Playgroud)
MSVC编译错误:
<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)'
Run Code Online (Sandbox Code Playgroud)
这是MSVC中的错误吗?
我认为这基本上是CWG 2327的体现,其中涉及此示例:
struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);
Run Code Online (Sandbox Code Playgroud)
问题的症结在于,在这种情况下,我们不允许保证复制省略-因为我们通过Cat的move构造函数,而不是Dog::operator Cat()直接通过via进行初始化。
看来gcc和clang都已经实现了问题的意图 -即要同时对构造函数和转换函数进行重载解析。
在您的示例中:
Bar b2(foo);
Run Code Online (Sandbox Code Playgroud)
按照标准的信中,我们考虑构造函数(和唯一的构造函数) -这是Bar(X),Bar(Bar const&)和Bar(Bar&&)。所有这三个都是可行的,第一个通过Foo::operator X() const,第二个和第三个通过Foo::operator Bar() const。我们可能更愿意Bar(Bar&&),Bar(Bar const&)但我们有办法消除Bar(X)和之间的歧义Bar(Bar&&)。MSVC遵循正确拒绝此初始化的标准。这不是错误。
但是CWG 2327的精神是,这应该Foo::operator Bar() const直接调用,这就是gcc和clang所做的。很难说这是他们一方的错误,因为这可能是我们实际上想要发生的行为,并且很可能是将来某个时候指定它的方式。