为避免对临时对象进行不必要的拷贝,C++编译器常使用一种名为Copy Ellision(拷贝去除)的优化技术,该技术至少包括以下两项内容:
- 返回值优化(RVO),即通过将返回值所占空间的分配地点从被调用端转移至调用端的手段来避免拷贝操作。
返回值优化包括具名返回值优化(NRVO)与无名返回值优化(URVO),两者的区别在于返回值是具名的局部变量还是无名的临时对象。 - 右值拷贝优化,当某一个类类型的临时对象被拷贝赋予同一类型的另一个对象时,通过直接利用该临时对象的方法来避免拷贝操作。
这项优化只能用于右值(临时对象),不能用于左值。
如果需要测试编译器是否应用了Copy Ellision优化技术,可使用以下代码(摘自
cppnext)。
#include <iostream>
struct X
{
X() : id(instances++)
{
std::cout << "X" << id << ": construct\n";
}
X(X const& rhs) : id(instances++)
{
std::cout << "X" << id << ": <- " << "X" << rhs.id << ": **copy**\n";
++copies;
}
// This particular test doesn't exercise assignment, but for
// completeness:
X& operator=(X const& rhs)
{
std::cout << "X" << id << ": <- " << "X" << rhs.id << ": assign\n";
}
~X() { std::cout << "X" << id << ": destroy\n"; }
unsigned id;
static unsigned copies;
static unsigned instances;
};
unsigned X::copies = 0;
unsigned X::instances = 0;
#define CHECK_COPIES( stmt, min, max, comment ) \
{ \
unsigned const old_copies = X::copies; \
\
std::cout << "\n" comment "\n" #stmt "\n===========\n"; \
{ \
stmt; \
} \
unsigned const n = X::copies - old_copies; \
if (n > max) \
std::cout << "*** max is too low or compiler is buggy ***\n"; \
if (n < min) \
std::cout << "*** min is too high or compiler is buggy ***\n"; \
\
std::cout << "-----------\n" \
<< n << "/" << max \
<< " possible copies made\n" \
<< max - n << "/" << max - min \
<< " possible elisions performed\n\n"; \
\
if (n > min) \
std::cout << "*** " << n - min \
<< " possible elisions missed! ***\n"; \
}
struct trace
{
trace(char const* name)
: name(name)
{
std::cout << "->: " << name << "\n";
}
~trace()
{
std::cout << "<-: " << name << "\n";
}
char const* name;
};
void sink(X a)
{
trace t("sink");
}
X nrvo_source()
{
trace t("nrvo_source");
X a;
return a;
}
X urvo_source()
{
trace t("urvo_source");
return X();
}
X identity(X a)
{
trace t("identity");
return a;
}
X lvalue_;
X& lvalue()
{
return lvalue_;
}
typedef X rvalue;
int main()
{
// Double parens prevent "most vexing parse"
CHECK_COPIES( X a(( lvalue() )), 1, 1, "Direct initialization from lvalue");
CHECK_COPIES( X a(( rvalue() )), 0, 1, "Direct initialization from rvalue");
CHECK_COPIES( X a = lvalue(), 1, 1, "Copy initialization from lvalue" );
CHECK_COPIES( X a = rvalue(), 0, 1, "Copy initialization from rvalue" );
CHECK_COPIES( sink( lvalue() ), 1, 1, "Pass lvalue by value" );
CHECK_COPIES( sink( rvalue() ), 0, 1, "Pass rvalue by value" );
CHECK_COPIES( nrvo_source(), 0, 1, "Named return value optimization (NRVO)" );
CHECK_COPIES( urvo_source(), 0, 1, "Unnamed return value optimization (URVO)" );
// Just to prove these things compose properly
CHECK_COPIES( X a(urvo_source()), 0, 2, "Return value used as ctor arg" );
// Expect to miss one possible elision here
CHECK_COPIES( identity( rvalue() ), 0, 2, "Return rvalue passed by value" );
}
代码说明
- lvalue()函数返回struct X类型的全局变量lvalue_的引用,故lvalue()函数返回左值,可用于测试右值拷贝优化。
- rvalue()函数返回struct X类型的构造器所构建的临时对象,故rvalue()函数返回右值,可用于测试右值拷贝优化。
- nrvo_source()函数返回struct X类型的局部变量a,故nrvo_source()函数可用于测试具名返回值优化。
- urvo_source()函数返回struct X类型的构造器所构建的临时对象,故urvo_source()函数可用于测试无名返回值优化。
- 第109,110行分别用左右值来测试类对象直接初始化中的右值拷贝优化。
- 第112,113行分别用左右值来测试类对象拷贝初始化中的右值拷贝优化。
- 第115,116行分别用左右值来测试普通函数实参中的右值拷贝优化。
- 第118,119行分别用于测试具名返回值优化与无名返回值优化。
X0: construct
Direct initialization from lvalue
X a(( lvalue() ))
===========
X1: <- X0: **copy**
X1: destroy
-----------
1/1 possible copies made
0/0 possible elisions performed
Direct initialization from rvalue
X a(( rvalue() ))
===========
X2: construct
X2: destroy
-----------
0/1 possible copies made
1/1 possible elisions performed
Copy initialization from lvalue
X a = lvalue()
===========
X3: <- X0: **copy**
X3: destroy
-----------
1/1 possible copies made
0/0 possible elisions performed
Copy initialization from rvalue
X a = rvalue()
===========
X4: construct
X4: destroy
-----------
0/1 possible copies made
1/1 possible elisions performed
Pass lvalue by value
sink( lvalue() )
===========
X5: <- X0: **copy**
->: sink
<-: sink
X5: destroy
-----------
1/1 possible copies made
0/0 possible elisions performed
Pass rvalue by value
sink( rvalue() )
===========
X6: construct
->: sink
<-: sink
X6: destroy
-----------
0/1 possible copies made
1/1 possible elisions performed
Named return value optimization (NRVO)
nrvo_source()
===========
->: nrvo_source
X7: construct
<-: nrvo_source
X7: destroy
-----------
0/1 possible copies made
1/1 possible elisions performed
Unnamed return value optimization (URVO)
urvo_source()
===========
->: urvo_source
X8: construct
<-: urvo_source
X8: destroy
-----------
0/1 possible copies made
1/1 possible elisions performed
Return value used as ctor arg
X a(urvo_source())
===========
->: urvo_source
X9: construct
<-: urvo_source
X9: destroy
-----------
0/2 possible copies made
2/2 possible elisions performed
Return rvalue passed by value
identity( rvalue() )
===========
X10: construct
->: identity
X11: <- X10: **copy**
<-: identity
X11: destroy
X10: destroy
-----------
1/2 possible copies made
1/2 possible elisions performed
*** 1 possible elisions missed! ***
X0: destroy