使用纯右值直接初始化:MSVC 中的错误?

Rup*_*rrt 5 c++ list-initialization c++17 prvalue direct-initialization

考虑以下代码:

struct S
{
    S(int, double) {}
    explicit S(const S&) {}
    explicit S(S&&) {}
};

void i_take_an_S(S s) {}

S i_return_an_S() { return S{ 4, 2.0 }; }

int main()
{
    i_take_an_S(i_return_an_S());
}

Run Code Online (Sandbox Code Playgroud)

使用“-std=c++17”标志,g++ 和 clang++ 都可以很好地编译此代码。然而,MSVC(带有 /std:c++17 标志)报告

“错误 C2664:'void i_take_an_S(S)':无法将参数 1 从 'S' 转换为 'S'”

作为编译错误,带有附加注释

“结构‘S’的构造函数被声明为‘显式’”。

根据C++17的初始化规则(第3点的解释S,不应考虑使用复制构造函数来S初始化i_take_an_SS(int, double)应该通过直接列表初始化选择精确匹配。

这可能是 MSVC 中的一个错误吗?

use*_*522 2

是的,MSVC在这里似乎是错误的。

一般来说,从C++17开始,初始化规则是S{ 4, 2.0 }直接初始化S s函数的参数。(强制复制省略)

不过,有一个例外。如果类类型仅具有删除的或简单的复制/移动构造函数和析构函数(以及至少其中一个未删除的),则允许实现在函数参数或返回值中引入副本。

您声明复制和移动构造函数explicit不会改变它们是复制/移动构造函数。因为您不是用来= default定义它们的,所以它们并不是微不足道的。因此,特殊权限不适用,MSVC 尝试执行复制是错误的。

此外,这种特殊类型的复制会忽略可访问性和重载解析,因此explicit即使执行它也不应该相关,请参阅[class.temporary]/3


然而,当执行精确复制省略时会影响 ABI,因此如果这是 MSVC ABI 中的缺陷,那么它可能不容易修复。