赋值初始化是否应该与具有非显式单参数构造函数但具有删除的移动构造函数的类型一起使用?

Should assignment initialization work with a type with non-explicit single param ctor but with deleted move ctor?

下面的代码应该在c++11规则下编译吗?为什么应该或为什么不?有UB吗?似乎 gcc 之前禁止它,但在版本 11 中改变了主意。Microsoft 接受它,而 clang 始终不接受。

我的意思是 IntWrapper myInt = 42; 在这种情况下只是语法糖,与 IntWrapper myInt(42); 完全相同,赋值运算符的重载对初始化没有影响。在我看来,旧版本的 gcc 和所有版本的 clang 都想做 ctor(int),然后是 move。虽然 msvc 和新版本 gcc 只是调用 ctor(int) 我认为是正确的。为什么要在不需要的时候移动。

如果某些 c++ 语言律师可以将其翻译成英文,请在此处查看 11.10.1 显式初始化第 294-295 页 https://isocpp.org/files/papers/N4860.pdf

Alternatively, a single assignment-expression can be specified as an initializer using the = form of initialization. Either direct-initialization semantics or copy-initialization semantics apply;

Note: Overloading of the assignment operator (12.6.2.1) has no effect on initialization. —

我对标准的理解是,编译器可以选择先复制然后移动,或者直接使用接受一个参数的 ctor 进行初始化。这会很奇怪,因为你怎么知道它是否编译。

#include <iostream>

struct IntWrapper
{
  IntWrapper(int value) : m_value(value)
  {
      std::cout << "ctor(int)\n";
  }

  IntWrapper(IntWrapper&& that) = delete;
  int m_value;
};

int main()
{
    IntWrapper myInt = 42;
    return 0;
}
compiler result
msvc v.19.x compiles
gcc 11.x compiles
gcc 10.x error: use of deleted function 'IntWrapper::IntWrapper(IntWrapper&&)
clang 14.0.0 error: copying variable of type 'IntWrapper' invokes deleted constructor
clang 13.0.0 error: copying variable of type 'IntWrapper' invokes deleted constructor
clang 12.0.0 error: copying variable of type 'IntWrapper' invokes deleted constructor

msvc v.19.x 和 gcc 11.x 都默认使用 C++17 作为编译语言标准,而您使用的所有其他编译器默认使用 C++14。这就是您看到差异的原因。

在 C++17 之前 IntWrapper myInt = 42; 在语义上被视为 IntWrapper myInt = IntWrapper(42); 所以你需要一个 non-deleted 复制或移动构造函数来编译它,即使临时对象被省略通过编译器优化。

由于 C++17 IntWrapper myInt = 42; 现在被视为已经完成 IntWrapper myInt{42}; 现在我们直接构造,没有创建临时对象。此功能称为


当标准说

Either direct-initialization semantics or copy-initialization semantics apply;

它们意味着 IntWrapper myInt = 42; 可以被视为 IntWrapper myInt = IntWrapper(42);IntWrapper myInt{42};。这取决于您是否打开了优化,取决于您获得的是哪种形式。可以在 [class.temporary]/1

中找到使 C++17 之前失败的标准的附加部分

Even when the creation of the temporary object is unevaluated (Clause [expr]) or otherwise avoided ([class.copy]), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed. [ Note: This includes accessibility ([class.access]) and whether it is deleted, for the constructor selected and for the destructor. However, in the special case of a function call used as the operand of a decltype-specifier ([expr.call]), no temporary is introduced, so the foregoing does not apply to the prvalue of any such function call. — end note ]