C++17 在 MOVE 省略方面发生了什么变化

What has changed in C++17 in terms of MOVE elision

C++17 是否保证“移动省略”?让我解释一下我的意思。在几乎每一篇关于 C++17 介绍的文章中,都可以找到术语:“RVO 的保证复制省略”,这有点不言自明。但是移动构造呢?

我们看下面的代码,很简单,有一个不可复制的类型,两个函数,一个按值取NonCopyable参数,第二个按右值引用取。

#include <iostream>

struct NonCopyable
{
  NonCopyable() = default;
  NonCopyable(const NonCopyable&) = delete;

  NonCopyable(NonCopyable&& other) noexcept
  {
    std::cout << "Move ctor\n";
  }
};

void func_by_value(NonCopyable cp)
{
  auto x = std::move(cp);
}

void func_by_rvalue_ref(NonCopyable&& cp)
{
  auto x = std::move(cp);
}

int main() 
{
  std::cout << "Pass by value:\n";
  func_by_value(NonCopyable());

  std::cout << "\nPass by rvalue_ref\n";
  func_by_rvalue_ref(NonCopyable());
}

我使用 GCC(trunk) 使用以下标志编译了两次,结果略有不同。

(1) -O0 -std=c++11 -fno-elide-constructors

Program output: 

Pass by value:
Move ctor
Move ctor

Pass by rvalue_ref
Move ctor

(2) -O0 -std=c++17 -fno-elide-constructors

Program output:

Pass by value:
Move ctor

Pass by rvalue_ref
Move ctor

所以我的问题是 - 使用 C++17 时移动限制被删除有什么变化? Compiler explorer

自引入 C++17 mandatory elision of copy/move operations 以来:

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

  • ...

  • In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:

    T x = T(T(f())); // only one call to default constructor of T, to initialize x
    

在从纯右值NonCopyable()初始化参数cp时,需要省略move构造。请注意,强制复制省略适用于复制操作和移动操作。