C++ Copy Elision
故事得从 copy/move constructor 说起:
The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor and move assignment operator (12.8), and destructor (12.4) are special member functions. [ Note: The implementation will implicitly declare these member functions for some class types when the program does not explicitly declare them. The implementation will implicitly define them if they are odr-used (3.2). See 12.1, 12.4 and 12.8. — end note ]
上面这段文字来自 C++11 Standard 中 “12 Special member functions”,关于什么时候使用 copy/move constructor 什么时候使用 copy/move assignment operator,在 “12.8 Copying and moving class objects” 中第一段有详细的说明:
A class object can be copied or moved in two ways: by initialization (12.1, 8.5), including for function argument passing (5.2.2) and for function value return (6.6.3); and by assignment (5.17). Conceptually, these two operations are implemented by a copy/move constructor (12.1) and copy/move assignment operator (13.5.3).
也就是说:在初始化、函数参数传递和函数值返回的时候将会使用到 copy/move constructor,而在赋值的时候才会使用到 copy/move assignment operator
一、copy/move constructor
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).
类 X 的 copy constructor 是一个非模板构造函数,该函数的第一个参数必须是 X&、const X&、volatile X& 或者 const volatile X&,如果还有其他参数,其他参数必须有默认值
为了简化问题,这里我们不讨论 move constructor,也就是说,假设 gcc 版本为 4.1.2 并且不支持 C++0x 提出的 move semantic。看看下面这段例子:
#include <iostream>
struct X {
X() { std::cout << "default constructor" << std::endl; }
~X() { std::cout << "destructor" << std::endl; }
X(const X&) { std::cout << "copy constructor\n"; }
X& operator=(const X&) { std::cout << "copy assignment operator\n"; }
};
int main() {
X a; // initialization, use default constructor
X aa(a); // initialization, use copy constructor
X aaa = a; // initialization, use copy constructor
aa = a; // assignment, use copy assignment operator
return 0;
}
按 Standard 所说,上面代码的行为应该是和注释一样,于是我们编译并运行试试:
$ g++ a.cpp -o a
$ ./a
default constructor
copy constructor
copy constructor
copy assignment operator
destructor
destructor
destructor
结果的确是和预期的一致,那么再来看看需要使用 copy constructor 的另外一种情况 “function value return”,这里不涉及利用函数返回值初始化另一个对象的情况,只是单纯的调用函数:
#include <iostream>
struct X {
X() { std::cout << "default constructor" << std::endl; }
~X() { std::cout << "destructor" << std::endl; }
X(const X&) { std::cout << "copy constructor\n"; }
X& operator=(const X&) { std::cout << "copy assignment operator\n"; }
};
X f() {
return X();
}
int main() {
f();
return 0;
}
编译并运行,其结果如下:
$ g++ a.cpp -o a
$ ./a
default constructor
destructor
预期的那次 copy constructor 调用并没有出现。这里不得不说到一个编译器优化:return value optimization
二、return value optimization
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. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.126 This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration.
这里注意第一段就行了:某些场景下,编译器可以省略一次 copy/move construction,不管有没有 side effect。省略 copy/move construction 能带来什么 side effect 呢?copy/move constructor 里的代码不会执行(比如上面例子中的 cout 信息)。Standard 给出几个省略 copy/move construction 的场景,场景一就是上面的情况:
函数的 return 语句中的表达式是一个非 volatile 对象的名字,并且其 cv-unqualified type 和函数返回值的 cv-unqualified type 相同,此时可以省略一次 copy/move construction。
那么什么是 cv-unqualified type 和 cv-qualified type 呢?如果有耐心的话,Standard 里也是有讲的,在第 3.9.3 节,这里我就不贴原文了,简单的说就是:"cv" 分别指的是 "const" 和 "volatile",cv-unqualified type 指的是没有这两个修饰符修饰的类型。可以看看 Stack Overflow 上面的解释,简单精确:What does "cv-unqualified" mean in C++?
现在就能够解释上面例子中的行为了:函数返回值被保存到了一个临时变量里,而构造这个临时变量调用的是类 X 的 copy/move constructor,传入的参数是 return 语句后面的表达式,巧合的是,临时变量和传入参数的 cv-unqualified type 相同,因此 gcc 把这次 copy/move construction 省略掉了(或者说优化掉了),而在 copy/move constructor 中的 cout 代码就自然不被执行了,这就是省略 copy/move construction 带来的 side effect 吧。
gcc 提供了一个编译选项:-fno-elide-constructors
,用它能够关闭 gcc 省略 copy/move construction 的默认行为,所以,如果我们这样编译代码并运行的话,就能够看见期望看见的那次 copy/move construction 了:
$ g++ a.cpp -o a -fno-elide-constructors
$ ./a
default constructor
copy constructor
destructor
destructor
关于 return value optimization,可以看看维基百科:Return value optimization
三、Copy Elision
让我们更近一步,如果细心的话可能已经发现,在上面贴出来的省略 copy/move construction 的条件里还有这么一条:
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
当用一个临时对象初始化另一个对象的时候,如果他们俩的 cv-unqualified type 相同,并且临时对象没有和任何引用绑定,那么此次 copy/move construction 也是可以省略的:
#include <iostream>
struct X {
X() { std::cout << "default constructor" << std::endl; }
~X() { std::cout << "destructor" << std::endl; }
X(const X&) { std::cout << "copy constructor\n"; }
X& operator=(const X&) { std::cout << "copy assignment operator\n"; }
};
X f() {
return X();
}
int main() {
X a = f();
return 0;
}
如果我们直接编译运行的话,那两次 copy/move construction 肯定都被优化掉了:
$ g++ a.cpp -o a
$ ./a
default constructor
destructor
如果加上 -fno-elide-constructors 这个选项:
$ g++ a.cpp -o a -fno-elide-constructors
$ ./a
default constructor
copy constructor
destructor
copy constructor
destructor
destructor
为了作对比,如果不用那个临时变量初始化一个 X 对象,而是先把它赋值给一个 X 对象 a,然后用 a 来 copy initialize 一个 X 对象 b,那么初始化 b 的那次 copy construction 是不会被省略的:
#include <iostream>
struct X {
X() { std::cout << "default constructor" << std::endl; }
~X() { std::cout << "destructor" << std::endl; }
X(const X&) { std::cout << "copy constructor\n"; }
X& operator=(const X&) { std::cout << "copy assignment operator\n"; }
};
X f() {
return X();
}
int main() {
X a = f();
X b = a;
return 0;
}
编译并运行:
$ g++ a.cpp -o a
$ ./a
default constructor
copy constructor
destructor
destructor
结果和预想的一致,copy elision 大致就解释完了,有空可以看看维基百科:Copy elision
C++ Copy Elision的更多相关文章
- Copy elision in C++
Copy elision (or Copy omission) is a compiler optimization technique that avoids unnecessary copying ...
- C++的Copy Elision导致的奇怪问题
最近写设计模式作业的时候, 有一个作业是实现装饰器模式 (Decorator Pattern), 由于我不会 Java, 所以只能用 C++ 来实现 在这个背景下, 会有简单(表意)的几个类, 如下: ...
- copy elision
http://book.51cto.com/art/200810/93007.htm 1.2.2 数据传送指令 mov:数据移动.第一个参数是目的,第二个参数是来源.在C语言中相当于赋值号.这是最广 ...
- copy elison & RVO & NRVO
蓝色的博文 To summarize, RVO is a compiler optimization technique, while std::move is just an rvalue cast ...
- Google C++ Style Guide
Background C++ is one of the main development languages used by many of Google's open-source project ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part6:Move语义和编译器优化
本文为第六部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- move和转发
总的来说C++09跟C++98相比的变化是极其重大的.这个变化体现在三个方面,一个是形式上的变化,即在编码形式层面的支持,也就是对应我们所谓的编程范式(paradigm).C++09不会引入新的编程范 ...
- C++临时对象以及针对其进行的优化
C++临时对象以及针对其进行的优化 C++中真正的临时对象是看不见的,它们不出现在你的源代码中. 那么什么时候回产生临时对象呢?主要是三个时刻: 产生临时对象的三个时刻: 用构造函数作为隐式类型转换函 ...
- Google C++ 代码规范
Google C++ Style Guide Table of Contents Header Files Self-contained Headers The #define Guard For ...
随机推荐
- 微软发布Windows 10:连Windows 7都能免费升级了
今日凌晨,微软宣布Windows 10发布. Windows10 整体重置了 Window8 的设计,恢复了原有的开始菜单设置,系统新增了虚拟桌面功能,搜索框加分类项的形式替代原有的两栏式控制面板界面 ...
- nginx location模块--匹配规则
Location语法语法:location [=|~|~*|^~] /uri/ { … } = --> 开头表示精确匹配 ^~ --> 开头表示uri以某个常规字符串开头,理解为匹配url ...
- ServiceStack 概念
目录 ServiceStack 概念 ServiceStack Web Service 创建与调用简单示列 ServiceStack ServiceStack是.Net和Mono的开源框架,相对WCF ...
- 理解Certificate、App Id、Identifiers 和 Provisioning Profile
做真机测试的时候,按照网上的流程,走通了,当时没有注意各种证书等的意思.现在做消息推送,需要各种证书.APP ID信息,为了更好的理解这个过程,所以整理了网上关于证书等的相关资料.方便自己和有需要的朋 ...
- 上海SAP代理商 电子行业ERP系统 SAP金牌代理商达策
上海SAP代理商 电子行业ERP系统 SAP金牌代理商达策上海达策为电子行业企业提供了多样的ERP信息化管理系统.基于多营运中心的管理架构体系,构造了以供应链.生产管理.财务一体化为核心,协同HR.B ...
- python的反射
目前大多数网站都是通过路由的方法来,处理url请求,如果有很多个url的话,不停的include或者用if判断匹配,似乎不太符合情理,因此这里讲讲python的反射机制, 自动装在模块.请看下面的实例 ...
- spark spark ziliao important
http://book.51cto.com/art/201408/448416.htm 一.如何实现多台机器的ssh无密码登录 当我们在配置多台计算,使之可以相互使用无密码登录-ssh,之前都是一台一 ...
- android: 文件存储
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑 关机的情况下,这些数据仍然不会丢失.保存在内存中的数据是处于瞬时状态的,而保存在 存储设备中的数据是处于持久状态的,持久化 ...
- phpweb成品网站最新版(注入、上传、写shell)
注入:之所以鸡肋就是该漏洞利用安装文件 重新生成 配置文件 写入可执行代码 鸡肋1: 具有破坏性 动作非常大 重新写了配置文件 数据库连接文件鸡肋2: 有一定安全常识的站长都会删掉 install 目 ...
- redis的基本使用
Redis的应用 http://www.redis.cn/commands.html http://www.cnblogs.com/ikodota/archive/2012/03/05/php_red ...