'explicit' 关键字对 Return 价值优化 (RVO) 有何影响?

What is the effect of 'explicit' keyword on the Return Value Optimization (RVO)?

以下代码运行良好(显示 RVO):

struct A { 
  A (int) { cout << "A::A()\n"; }  // constructor
  A (const A&) { cout << "A::A(const A&)\n"; }  // copy constructor
};

A foo () { return A(0); }

int main () {
  A a = foo();
}

输出:

A::A()  // --> which means copy constructor is not called

如果我将复制构造函数标记为 explicit:

explicit A (const A&) { ... }

然后编译器报错:

explicit.cpp: In function ‘A foo()’:
explicit.cpp:10:22: error: no matching function for call to ‘A::A(A)’
 A foo () { return A(0); }
                      ^
explicit.cpp:5:3: note: candidate: A::A(int)
   A (int) { cout << "A::A()\n"; }
   ^
explicit.cpp:5:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’
explicit.cpp: In function ‘int main()’:
explicit.cpp:14:13: error: no matching function for call to ‘A::A(A)’
   A a = foo();
             ^
explicit.cpp:5:3: note: candidate: A::A(int)
   A (int) { cout << "A::A()\n"; }
   ^
explicit.cpp:5:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’

为什么会这样,RVO 不应该按原样工作吗?

RVO 可以省略副本,但语言规则要求副本(或移动)必须仍然是可能的:

[C++14: 12.8/31]: When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. [..]

[C++14: 12.8/32]: [..] [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]

您通过添加 explicit 使复制变得不可能,并且移动不可能,因为您的复制构造函数阻止了隐式定义的移动构造函数的创建。

您可以通过添加自己的移动构造函数来允许移动,也许是默认构造函数:

A(A&&) = default;

但这只是遵守相同语言规则的另一种方式。

C++17 无论如何都会放宽规则,通过添加一些不受此约束的复制省略保证。