什么时候重载按引用传递(左值和右值)优于按值传递?

2023-11-27

我见过它说operator=编写为按值获取相同类型的参数,可用作 C++11 中的复制赋值运算符和移动赋值运算符:

Foo& operator=(Foo f)
{
    swap(f);
    return *this;
}

如果替代方案的行数会增加两倍以上,并且有大量代码重复,并且可能会出现错误:

Foo& operator=(const Foo& f)
{
    Foo f2(f);
    swap(f2);
    return *this;
}

Foo& operator=(Foo&& f)
{
    Foo f2(std::move(f));
    swap(f2);
    return *this;
}

在什么情况下,引用常量和右值重载优于 按值传递,或者什么时候有必要?我在想std::vector::push_back, 例如,它被定义为两个重载:

void push_back (const value_type& val);
void push_back (value_type&& val);

按照第一个按值传递的示例用作副本分配 运算符和移动赋值运算符, 不能push_back被定义在 标准是单一功能吗?

void push_back (value_type val);

对于复制赋值运算符可以回收资源的类型,与副本进行交换几乎从来都不是实现复制赋值运算符的最佳方式。例如看看std::vector:

此类管理动态大小的缓冲区并维护capacity(缓冲区可以容纳的最大长度),以及size(当前长度)。如果vector复制赋值运算符已实现swap,那么无论如何,如果rhs.size() != 0.

然而,如果lhs.capacity() >= rhs.size(),根本不需要分配新的缓冲区。人们可以简单地分配/构造元素rhs to lhs。当元素类型是普通可复制的时,这可能归结为什么都没有,但memcpy。这可以很多,much比分配和取消分配缓冲区更快。

同样的问题std::string.

同样的问题MyType when MyType具有以下数据成员std::vector and/or std::string.

只有 2 次您需要考虑使用交换来实现复制分配:

  1. 你知道swap方法(包括当 rhs 是左值时的强制复制构造)不会非常低效。

  2. 你知道你会always需要复制赋值运算符有较强的异常安全保证。

如果您不确定 2,换句话说您认为复制赋值运算符可能有时需要强有力的异常安全保障,不要以交换的方式实现赋值。如果您提供以下之一,您的客户很容易获得相同的保证:

  1. 无例外交换。
  2. noexcept 移动赋值运算符。

例如:

template <class T>
T&
strong_assign(T& x, T y)
{
    using std::swap;
    swap(x, y);
    return x;
}

or:

template <class T>
T&
strong_assign(T& x, T y)
{
    x = std::move(y);
    return x;
}

现在,在某些类型中,通过交换实现复制分配是有意义的。然而,这些类型只是例外,而不是规则。

On:

void push_back(const value_type& val);
void push_back(value_type&& val);

Imagine vector<big_legacy_type> where:

class big_legacy_type
{
 public:
      big_legacy_type(const big_legacy_type&);  // expensive
      // no move members ...
};

如果我们只有:

void push_back(value_type val);

Then push_back计算左值big_legacy_type into a vector需要 2 份而不是 1 份,即使capacity就足够了。从性能角度来看,这将是一场灾难。

Update

这是一个 HelloWorld,您应该能够在任何符合 C++11 的平台上运行:

#include <vector>
#include <random>
#include <chrono>
#include <iostream>

class X
{
    std::vector<int> v_;
public:
    explicit X(unsigned s) : v_(s) {}

#if SLOW_DOWN
    X(const X&) = default;
    X(X&&) = default;
    X& operator=(X x)
    {
        v_.swap(x.v_);
        return *this;
    }
#endif
};

std::mt19937_64 eng;
std::uniform_int_distribution<unsigned> size(0, 1000);

std::chrono::high_resolution_clock::duration
test(X& x, const X& y)
{
    auto t0 = std::chrono::high_resolution_clock::now();
    x = y;
    auto t1 = std::chrono::high_resolution_clock::now();
    return t1-t0;
}

int
main()
{
    const int N = 1000000;
    typedef std::chrono::duration<double, std::nano> nano;
    nano ns(0);
    for (int i = 0; i < N; ++i)
    {
        X x1(size(eng));
        X x2(size(eng));
        ns += test(x1, x2);
    }
    ns /= N;
    std::cout << ns.count() << "ns\n";
}

我已经编码了X的复制赋值运算符有两种方式:

  1. 隐式的,相当于调用vector的复制赋值运算符。
  2. 用复制/交换惯用语,建议在宏下SLOW_DOWN。我想过给它命名SLEEP_FOR_AWHILE,但如果您使用的是电池供电的设备,这种方式实际上比睡眠语句要糟糕得多。

该测试构建了一些随机大小的vector<int>s 介于 0 和 1000 之间,并对它们赋值一百万次。它对每个时间进行计时,将时间相加,然后找到以浮点纳秒为单位的平均时间并将其打印出来。如果对高分辨率时钟的两次连续调用没有返回小于 100 纳秒的时间,您可能需要增加向量的长度。

这是我的结果:

$ clang++ -std=c++11 -stdlib=libc++ -O3 test.cpp
$ a.out
428.348ns
$ a.out
438.5ns
$ a.out
431.465ns
$ clang++ -std=c++11 -stdlib=libc++ -O3 -DSLOW_DOWN test.cpp
$ a.out
617.045ns
$ a.out
616.964ns
$ a.out
618.808ns

通过这个简单的测试,我发现复制/交换习惯的性能下降了 43%。 YMMV。

平均而言,上述测试在一半的时间里 lhs 具有足够的容量。如果我们采取任一极端:

  1. lhs始终有足够的容量。
  2. lhs 始终没有足够的容量。

那么默认复制分配相对于复制/交换习惯的性能优势从大约 560% 到 0% 不等。复制/交换习惯永远不会更快,并且可能会显着变慢(对于此测试)。

想要速度吗?措施。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么时候重载按引用传递(左值和右值)优于按值传递? 的相关文章

随机推荐

  • 飞行路线信息屏幕状态是未来,而不是缺失

    由于一些公司特定的功能 我需要换入和换出 有时当我稍后运行 info 或 migrate 时 我会迁移一些不存在于 sql 目录中的脚本 不过 我刚刚注意到它的显示方式存在不一致之处 Version Description Installe
  • Android:ViewGroup,如何拦截MotionEvent然后分派到目标或按需吃掉它?

    假设有一个具有多个子项的 ViewGroup 至于这个 ViewGroup 我想让它管理其所有子项的所有 MotionEvent 这表示 VG 将1 能够在将所有事件分派到目标 子级 之前拦截所有事件2 VG首先会消费该事件 并判断是否进一
  • GCC makefile依赖生成路径

    我用 MMGCC 中用于生成对象的 makefile 依赖项的标志 makefile 简要如下所示 include autodep TARGET build OBJECTS CC shared o OBJECTS CC MM SOURCES
  • 如何从多个源文件创建 MSBuild 内联任务

    我有几个 CS 文件 一个 DLL 项目 全部位于一个目录中 其中的类之一扩展了 ITask 现在 如何从一个源文件创建内联任务很容易并有记录 但是是否可以从多个源文件执行此操作 我无法将编译和使用 DLL 作为一项任务 并且我希望不必将所
  • BigNums 实施如何工作?

    我想知道 BigInt 和其他类似的东西是如何实现的 我试图查看 JAVA 源代码 但对我来说全是希腊语和拉丁语 您能否用文字向我解释一下该算法 没有代码 以便我了解当我使用 JAVA API 中的某些内容时我实际使用的是什么 问候 从概念
  • 如何创建自定义 jquery 轮播?

    您好 我想为我的 php Web 应用程序创建一个简单的 jquery 轮播 我正在使用 php5 和 mysql 我怎样才能让任何人都能帮助我 尝试这个教程 http jqueryfordesigners com jquery infin
  • 仅当窗口未打开时才Window.open

    我的网站上有一个链接 可以打开一个新窗口 转到播放很长音频文件的页面 我当前的脚本可以正常打开页面 并且如果多次单击链接则不会刷新 但是 当我移至网站上的单独页面并再次单击此链接时 它会重新加载 我知道当父元素更改时 我将丢失变量 因此我需
  • GNU C 中的 __attribute__((const)) 与 __attribute__((pure))

    有什么区别 attribute const and attribute pure 在 GNU C 中 attribute const int f return 4 vs attribute pure int f return 4 来自ARM
  • Qt 5 中的屏幕键盘

    我想为桌面应用程序创建一个屏幕键盘 该应用程序将在 Qt 5 中构建 我有几个问题 请澄清 什么是替代品QInputContext在 Qt5 中 因为我在某处读到过关于屏幕键盘的实现QInputContext但 Qt 5 不支持此功能 我在
  • 为什么 ForEach-Object 中的 continue 操作与中断类似? [复制]

    这个问题在这里已经有答案了 arr 1 10 arr ForEach Object if eq 5 continue output Result output 1 output 2 output 3 output 4 arr 1 10 ar
  • kotlin 中的递归类型参数

    我想用 Kotlin 写一些类似的东西 open class View p where P Presenter p
  • 检测硬件类型的最佳方法是 iPhone 4 还是 iPhone 5?

    我正在设置一个应用程序 我想根据所使用的 iPhone 类型对视图布局进行一些更改 具体来说 我想知道垂直屏幕分辨率是否为1136 iPhone5 或960 iPhone4 我想做的一件事是调整 UITableView 中 UITableV
  • 在 capistrano 任务中启动后台进程

    卡皮斯特拉诺任务 namespace service do desc start daemontools svscan supervise svscanboot task start roles gt app do sudo svscanb
  • 如何在游戏中实现碰撞效果?

    我用 QT 构建了一个游戏 我的 GraphicsScene 上的每个对象都继承自 GraphicsPixmapItem 玩家 障碍物 炸弹 我想实现碰撞效果 例如 当玩家将鼠标悬停在奖金上时 他可以选择它 使用 QT 框架 我可以获得碰撞
  • MongoDB 中的嵌套注释

    我对 MongoDB 很陌生 并尝试用它构建一个嵌套评论系统 在网上 您正在寻找各种文档结构来实现这一目标 但我正在寻找一些建议 这些建议将使我能够轻松地使用注释执行以下操作 将评论标记为垃圾邮件 已批准并通过此属性检索评论 检索用户的评论
  • 条件预处理器相对于条件语句的优点

    我从未与 if ifdef ifndef else elif and endif 当我浏览一些源代码时 我发现这些指令被广泛使用 阅读了一些有关条件预处理器的内容 但没有找到类似的线索它们与普通条件语句有何不同 所以我想知道以下代码有什么好
  • 正则表达式将单词与唯一(非重复)字符匹配

    我正在寻找一个正则表达式 仅当单词的所有字符都是唯一的时才匹配该单词 这意味着该单词中的每个字符仅出现一次 Example abcdefg gt 将返回MATCH abcdefgbh gt 将返回NO MATCH 因为信b重复多次 试试这个
  • 如何在vbscript中创建集合对象?

    参数应该是什么create object下面的代码 dim a set a CreateObject Collection getting a runtime error saying ActiveX component can t cre
  • ActionBarCompat - 如何使用它

    我正在尝试在我自己的项目中使用 ActionBarCompat 我已经打开了示例项目 http developer android com resources samples ActionBarCompat index html 但我不知道
  • 什么时候重载按引用传递(左值和右值)优于按值传递?

    我见过它说operator 编写为按值获取相同类型的参数 可用作 C 11 中的复制赋值运算符和移动赋值运算符 Foo operator Foo f swap f return this 如果替代方案的行数会增加两倍以上 并且有大量代码重复