'explicit'关键字对返回值优化(RVO)的影响是什么?

iam*_*ind 2 c++ compiler-errors explicit copy-constructor return-value-optimization

以下代码完美正常(显示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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

A::A()  // --> which means copy constructor is not called
Run Code Online (Sandbox Code Playgroud)

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

explicit A (const A&) { ... }
Run Code Online (Sandbox Code Playgroud)

然后编译器出错了:

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’
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况,RVO不应该按原样工作吗?

Lig*_*ica 7

RVO可以删除副本,但语言规则要求仍然可以复制(或移动):

[C++14: 12.8/31]:当满足某些条件时,允许实现省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用.[..]

[C++14: 12.8/32]: [..] [注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数. - 尾注]

您通过添加使副本无法进行explicit,并且无法进行移动,因为您的复制构造函数会阻止创建隐式定义的移动构造函数.

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

A(A&&) = default;
Run Code Online (Sandbox Code Playgroud)

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

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