为什么移动构造函数仅被调用一次?

时间:2018-12-12 06:22:43

标签: c++ constructor move

我具有以下功能:

   $update = tablename::where('id',$request->input('uid'))->where('status',1)->first();
    if ($request->hasFile('cover'))
    {
         File::delete(public_path().$update->pu_cover_photo);
    }

以及以下两种情况:

1

SomeClass func()
{
    SomeClass someObject;
    someObject.mutate("some text");
    return someObject;
}

2

int main()
{
    func();
    return 0;
}

我已关闭NRVO,没有复制/移动省略。

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

int main()
{
    SomeClass someObject = func();
    return 0;
}

为什么在情况2中move构造函数仅被调用一次?我本来希望函数的返回值被调用一次,而第二次初始化someObject变量。

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

2 个答案:

答案 0 :(得分:1)

这是由于C ++ 17中的复制省略。来自cppreference

  

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

另请参见复制初始化。同样来自cppreference

  

副本初始化的影响是:
  首先,如果T是类类型,并且初始化程序是prvalue表达式,其cv不合格类型与T是同一类,则使用初始化程序表达式本身而不是从其实例化的临时表达式来初始化目标对象:请参见copy省略号(自C ++ 17起)

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

在C ++ 14中,如果您不希望进行优化,则不会进行优化(请参见-fno-elide-constructors)。
Demo(使用GCC,但Clang产生相同的结果)

在C ++ 17中,它是强制性的,因此确实会发生。
Demo(同样,GCC,但Clang同意)

答案 1 :(得分:0)

因此,应该出现两种省略情况:一种用于return语句,另一种用于初始化。不加任何省略,输出应为:默认构造函数,移动构造函数,移动构造函数。一些编译器提供了禁用省略的选项。在带有-fno-elide-constructors的GCC上进行了测试,这一现象得到了证实。

现在,启用省略功能后,其行为尤其取决于所使用的C ++标准(C ++ 11 / C ++ 14 / C ++ 17),其次似乎也取决于编译器。自C ++ 11起发生了一些省略情况,自C ++ 14起发生了其他情况,而自C ++ 17起发生了其他情况。

在第2种情况下,GCC C ++ 14(和C ++ 11)同时使用了这两种方式,而MSVC C ++ 14编译器仅采用一种方式(尚未确定是哪一种)。