C++ 为什么move构造函数只调用一次?

C++ 为什么move构造函数只调用一次?,c++,constructor,move,C++,Constructor,Move,我有以下功能: SomeClass func() { SomeClass someObject; someObject.mutate("some text"); return someObject; } 以及以下两种情况: 1. 2. 我已关闭NRVO,未发生复制/移动省略 在这两种情况下,我的输出相同: "default constructor" "move constructor" 为什么在案例2中只调用一次move构造函数?我本来希望函数的返回值调用一次,然后初

我有以下功能:

SomeClass func()
{
    SomeClass someObject;
    someObject.mutate("some text");
    return someObject;
}
以及以下两种情况:

1. 2. 我已关闭NRVO,未发生复制/移动省略

在这两种情况下,我的输出相同:

"default constructor"
"move constructor"
为什么在案例2中只调用一次move构造函数?我本来希望函数的返回值调用一次,然后初始化someObject变量


更新:更清楚地说:输出用于调试生成。对于发布版本,我只有“默认构造函数”,这对我来说似乎很清楚,因为复制/移动省略。我想了解调试版本的不同输出。

这是由于C++17中的复制省略。发件人:

在以下情况下,即使复制/移动(自C++11)构造函数和析构函数有明显的副作用,编译器也允许但不要求省略类对象的复制和移动(自C++11)构造。
[…]
在对象初始化过程中,当源对象是无名临时对象且与目标对象具有相同的类类型(忽略cv限定)时。当匿名临时变量是return语句的操作数时,这种拷贝省略的变体称为RVO,“返回值优化”。
此优化是强制性的;见上文。(从C++17开始)

另请参见复制初始化。也来自:

复制初始化的效果是:
首先,如果T是一个类类型,并且初始值设定项是一个prvalue表达式,其cv非限定类型与T是同一个类,则初始值设定项表达式本身(而不是它的临时物化)用于初始化目标对象:请参见复制省略(自C++17)

请注意,这不是NRVO(命名的返回值优化)。这是RVO


在C++14中,如果不希望进行优化,则不会进行优化(请参见
-fno elide构造函数
)。
(使用GCC,但叮当会产生相同的结果)

在C++17中,它是强制性的,因此确实发生了。

(同样,GCC但Clang同意)

因此,应该出现两种省略情况:一种用于return语句,另一种用于初始化。没有任何省略,输出应该是:默认构造函数、移动构造函数、移动构造函数。某些编译器提供了禁用省略的选项。使用GCC和-fno-elide构造函数进行测试,并确认了此行为

现在,启用Eclipse,行为尤其依赖于C++标准(C++ 11/C++ 14/C++ 17),第二个也似乎在编译器上。从C++11开始出现了一些省略情况,从C++14开始出现了一些省略情况,从C++17开始出现了一些省略情况


在案例2中,GCC C++14(和C++11)应用了这两种省略,其中MSVC C++14编译器只应用了一种(尚未确定是哪一种)。

您是作为C++17构建的吗?如果NRVO在你身上的话,你根本就不会看到c'tor的行动。这是一个毫无根据的假设。即使在调试版本中,编译器仍会进行许多优化。主要是因为它们不影响可调试性。这并不奇怪。我询问您的构建选项(其中一个语言标准)是有原因的。编译器可以而且将要进行的优化类型取决于许多因素。你不能把它简化为调试版本和发布版本。老实说,如果调试版本决定在发布版本上关闭复制省略,我会非常惊讶。移动和复制构造函数可能有副作用,因此可能会导致代码行为不同。
g++-std=c++14-fno elide构造函数给我提供了
默认值
+
移动
+
移动
。切换到
c++17
将提供
默认值
+
移动
。删除
-fno elide构造函数
将导致
默认值
仅适用于c++14和17。是的,我同意你的看法。看来VS2017和GCC的行为有所不同。GCC只输出“默认构造函数”(可能是应该的),而VS2017输出“默认构造函数”+“移动构造函数”。@JollyRoger可能是因为它没有实现强制复制省略。看,它看起来像是在更新的版本中实现的。也许会更新你的编译器?但在所有的情况下你仍然有一个动作。我与GCC没有任何关系,只是在某些情况下与VS2017有关系。出现问题。我希望在复制/移动省略处于活动状态时不会移动。
-fno-elide构造函数
禁用GCC和Clang的复制省略。没有它,你当然不会有任何行动。
int main()
{
    SomeClass someObject = func();
    return 0;
}
"default constructor"
"move constructor"