已知下列函数定义:
X bar() {
X xx;
// process xx ...
return xx;
}
你可能会问bar()的返回值如何从局部对象xx中拷贝过来?
实现模型1
返回值的初始化(Return Value Initialization)
Stroustrup在cfront中的解决做法是一个双阶段转化:
- 首先加上一个额外参数,参数类型是对对象的引用,用来存放返回结果;
- 在return指令之前安插一个copy constructor调用操作,对这个参数利用返回值进行拷贝初始化。
根据上述算法,bar()的转换如下:
// function transformation to reflect
// application of copy constructor
// Pseudo C++ Code
void bar( X& __result )
{
X xx;
// compiler generated invocation
// of default constructor
xx.X::X();
// ... process xx
// compiler generated invocation
// of copy constructor
__result.X::X( xx );
return;
}
实现模型2
在编译器层面做优化(Optimization at the Compiler Level)
Named Return Value (NRV) optimization,具名返回值优化。
在一个像bar()这样的函数中,所有的return指令传回相同的具名数值,因此编译器有可能自己做优化,方法是:以result参数取代named return value。
根据这个方法,bar()的转换如下:
//__result is substituted for xx by the compiler:
void bar( X &__result )
{
// default constructor invocation
// Pseudo C++ Code
__result.X::X();
// ... process in __result directly
return;
}
Named Return value 优化:
NRV优化的本质是优化掉拷贝构造函数。在实现模型1中我们看到了,返回对象的实现总是先对某个对象进行操作,操作完成后,使用Copy Constructor将操作后的对象内容复制到另外一个对象中,然后返回。 基于这样一个前提,NRV可以调用Copy Constructor这一步被省掉。所以NRV的前提必须要有Copy Constructor。
NRV也有副作用,就是Copy Constructor中的代码不会被执行。
【注意】一方面要考虑默认的语义是否符合我们的需要。另一方面如果对象面临大量的拷贝操作[ 比如这个class的object需要经常以传值的方式返回],有必要实现一个拷贝构造函数以支持NRV优化。但是如果想使用底层的memcpy之类的直接进行bit wise copy,注意是否真的是bit wise copy拷贝,比如如果是virtual,这样可能破坏调vptr。