每周技巧 #24:拷贝,简写版

本节阅读量:

本文翻译自 Abseil 官网的 Tip of the Week #24: Copies, Abbrv.

原文最初作为 TotW #24 发布于 2012 年 11 月 26 日。

作者:Titus Winters 和 Chandler Carruth

“复制别人是必要的,但复制自己是可悲的。”– Pablo Picasso

注意:关于名称计数以及拷贝与移动的指导,也请参见 TotW #55TotW #77

一个名字,没有拷贝;两个名字,两份拷贝

评估任意给定作用域内是否会发生拷贝时(包括触发 RVO 的情况),请检查你的数据被多少个名字引用。

在任何时刻,只要你有两个活跃的名字指向这些数据副本,你就会有两份数据副本。 作为一个很好的第一近似,编译器会在所有其他情况下省略拷贝,而且往往也必须这样做。

在 STL 容器的移动语义(切换到 C++11 后自动引入)和编译器的拷贝构造省略共同作用下,我们正在迅速接近这样一种局面:这条规则不仅给出了拷贝次数的下界,而且就是保证。如果你的基准测试显示发生了更多拷贝,那很可能是编译器 bug;你的编译器大概需要修复。

所以,如果你的代码结构让某段执行过程中的某个时刻存在两个名字指向数据,就应该预期会发生拷贝。如果你避免引入一个可能指向这些数据的名字,就能帮助确保编译器可以移除拷贝。

示例

来看几个实践中的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
std::string build();

std::string foo(std::string arg) {
  return arg;  // 这里没有拷贝,数据只有一个名字 “arg”。
}

void bar() {
  std::string local = build();  // 只有 1 个实例,也只有 1 个名字

  // 没有拷贝,引用不会造成拷贝。
  std::string& local_ref = local;

  // 一次拷贝操作,现在有两份具名的数据集合。
  std::string second = foo(local);
}

大多数时候,这些都不重要。确保代码可读且一致,远比担心拷贝和性能更重要。一如既往:优化之前先做性能分析。不过,如果你正在从零开始写代码,并且能提供一个清晰、一致、返回值的 API,就不要因为它看起来像会发生拷贝而轻视它:十年前你学到的关于 C++ 拷贝的一切都已经错了。

每周技巧 #18:使用 Substitute 进行字符串格式化

上一节

每周技巧 #36:新的 Join API

下一节