这里不应该有一个copyctor调用吗?禁用省略(无命名返回值优化)

问题描述 投票:0回答:1
struct Test {
    int field = 30;
    Test() { cout << "In ctor" << endl; }
    Test(const Test &other) { field = other.field; cout << "In copy ctor" << endl; }
    Test(Test &&other) { field = other.field; cout << "In move ctor" << endl; }
    Test &operator=(const Test &other) { field = other.field; cout << "In copy assignment" << endl; return *this; }
    Test &operator=(Test &&other) { field = other.field; cout << "In move assignment" << endl; return *this; }
    ~Test() { cout << "In dtor" << endl; }
};

Test get_test() {
    Test t;
    return t;
}

int main() {
    Test t2 = get_test();
}

我认为这是典型的 NRVO 示例。我正在使用

-fno-elide-constructors
进行编译,我看到以下内容被称为:ctor、move ctor、dtor、dtor。

所以第一个ctor调用对应于行

Test t;
,移动ctor正在构造
t2
中的
main
,然后从
get_test
返回的临时对象被销毁,然后
t2
中的
main
被摧毁了。

我不明白的是:按值返回时不应该有复制构造函数调用吗?也就是说,我认为

get_test
应该制作
t
的副本,然后将此副本移至
t2
中。看起来
t
马上就移到了
t2

c++ move copy-constructor copy-elision nrvo
1个回答
3
投票

C++17

从 C++17 开始,有 mandatory copy elison ,上面写着:

在以下情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有明显的副作用。 对象直接构造到存储中,否则它们将被复制/移动到。复制/移动构造函数不需要存在或可访问:

  • 在对象的初始化中,当初始化表达式是与变量类型相同的类类型(忽略 cv 限定)的纯右值时。
  • 当操作数是与函数返回类型相同的类类型(忽略 cv 限定)的纯右值时,在 return 语句中初始化返回的对象:
T f()
{
   return U(); // constructs a temporary of type U,
               // then initializes the returned T from the temporary
}
T g()
{
   return T(); // constructs the returned T directly; no move
}

(强调我的)

这意味着

t2
是直接从
prvalue
返回的
get_test
构造的。由于使用
prvalue
来构造
t2
,因此使用了移动构造函数。请注意,在 C++17 中,标志
-fno-elide-constructors
对返回值优化(RVO) 没有影响,并且与 NRVO 不同。


C++17 之前

但在 C++17 之前,存在

非强制复制 elison,并且由于您提供了 -fno-elide-constructors

 标志,因此 
prvalue
 使用移动构造函数返回临时 
get_test
。所以你会看到对 move ctor 的第一个调用。然后,使用该临时对象再次使用移动构造函数初始化
t2
,因此我们第二次调用移动构造函数。

© www.soinside.com 2019 - 2024. All rights reserved.