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_S;S(int, double)应该通过直接列表初始化选择精确匹配。
这可能是 MSVC 中的一个错误吗?
是的,MSVC在这里似乎是错误的。
一般来说,从C++17开始,初始化规则是S{ 4, 2.0 }直接初始化S s函数的参数。(强制复制省略)
不过,有一个例外。如果类类型仅具有删除的或简单的复制/移动构造函数和析构函数(以及至少其中一个未删除的),则允许实现在函数参数或返回值中引入副本。
您声明复制和移动构造函数explicit不会改变它们是复制/移动构造函数。因为您不是用来= default定义它们的,所以它们并不是微不足道的。因此,特殊权限不适用,MSVC 尝试执行复制是错误的。
此外,这种特殊类型的复制会忽略可访问性和重载解析,因此explicit即使执行它也不应该相关,请参阅[class.temporary]/3。
然而,当执行精确复制省略时会影响 ABI,因此如果这是 MSVC ABI 中的缺陷,那么它可能不容易修复。