C++ 移动赋值可防止复制交换习惯用法

2023-11-26

在 C++ 中,复制交换惯用法通常是这样实现的:

C& operator=(C rhs)
{
    swap(*this, rhs);
    return *this;
}

现在,如果我想添加一个移动赋值运算符,它应该如下所示:

C& operator=(C&& rhs)
{
    swap(*this, rhs);
    return *this;
}

然而,这导致了应该调用哪个赋值运算符的歧义,并且编译器理所当然地抱怨它。所以我的问题如下:如果我想支持复制交换习惯用法和移动赋值语义,我应该做什么?

或者这不是问题,因为拥有移动复制构造函数和复制交换习惯用法,并没有真正从移动赋值运算符中受益?

在提出这个问题之后,我编写了一段代码,演示了移动赋值可能会比复制交换惯用语导致更少的函数调用。首先让我介绍一下我的复制交换版本。请多多包涵;它看起来像一个很长但简单的例子:

#include <algorithm>
#include <iostream>
#include <new>

using namespace std;

bool printOutput = false;

void* operator new(std::size_t sz)
{
    if (printOutput)
    {
        cout << "sz = " << sz << endl;
    }

    return std::malloc(sz);
}

class C
{
    int* data;

    public:

    C() : data(nullptr)
    {
        if (printOutput)
        {
            cout << "C() called" << endl;
        }
    }

    C(int data) : data(new int)
    {
        if (printOutput)
        {
            cout << "C(data) called" << endl;
        }

        *(this->data) = data;
    }

    C(const C& rhs) : data(new int)
    {
        if (printOutput)
        {
            cout << "C(&rhs) called" << endl;
        }

        *data = *(rhs.data);
    }

    C(C&& rhs) : C()
    {
        if (printOutput)
        {
            cout << "C(&&rhs) called" << endl;
        }

        swap(*this, rhs);
    }

    C& operator=(C rhs)
    {
        if (printOutput)
        {
            cout << "operator= called" << endl;
        }

        swap(*this, rhs);

        return *this;
    }

    C operator+(const C& rhs)
    {
        C result(*data + *(rhs.data));

        return result;
    }

    friend void swap(C& lhs, C& rhs);

    ~C()
    {
        delete data;
    }
};

void swap(C& lhs, C& rhs)
{
    std::swap(lhs.data, rhs.data);
}

int main()
{
    C c1(7);
    C c2;

    printOutput = true;

    c2 = c1 + c1;

    return 0;
}

我使用 -fno-elide-constructors 选项使用 g++ 编译了它,因为我想看到无优化行为。结果如下:

sz = 4
C(data) called   // (due to the declaration of result)
C() called       // (called from the rvalue copy-constructor)
C(&&rhs) called  // (called due to copy to return temporary)
C() called       // (called from the rvalue copy-constructor)
C(&&rhs) called  // (called due to pass-by-value in the assignment operator)
operator= called

现在,如果我选择不在赋值运算符中使用复制交换习惯用法,我将得到如下内容:

C& operator=(const C& rhs)
{
    if (printOutput)
    {
        cout << "operator=(const C&) called" << endl;
    }

    if (this != &rhs)
    {
        delete data;

        data = new int;
        *data = *(rhs.data);
    }

    return *this;
}

这允许我使用移动赋值运算符,如下所示:

C& operator=(C&& rhs)
{
    if (printOutput)
    {
        cout << "operator=(C&&) called" << endl;
    }

    swap(*this, rhs);

    return *this;
}

现在,在其他一切都相同的情况下,我得到以下输出:

sz = 4
C(data) called        // (due to the declaration of result)
C() called            // (called from the rvalue copy-constructor)
C(&&rhs) called       // (called due to copy to return temporary)
operator=(C&&) called // (move-assignment)

正如您所看到的,这会减少函数调用。实际上,copySwapIdiom 中的最后三个函数调用现在已降至单个函数调用。这是预期的,因为我们不再按值传递赋值运算符参数,因此那里不会发生构造。

然而,我并没有受益于赋值运算符中复制交换习惯用法的美妙之处。任何见解都非常感激。


如果您提供有效的移动构造函数,实际上不需要实现移动赋值运算符。

class Foo
{
public:
   explicit Foo(Bar bar)
       : bar(bar)
    { }

    Foo(const Foo& other)
        : bar(other.bar)
    { }

    Foo(Foo&& other)
        : bar(other.bar)
    { }

    // other will be initialized using the move constructor if the actual
    // argument in the assignment statement is an rvalue
    Foo& operator=(Foo other)
    {
        std::swap(bar, other.bar);
        return *this;
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 移动赋值可防止复制交换习惯用法 的相关文章

  • C++:无法使用scoped_allocator_adaptor传播polymorphic_allocator

    我有一个vector
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 为什么禁止在 constexpr 函数中使用 goto?

    C 14 对你能做什么和不能做什么有规则constexpr功能 其中一些 没有asm 没有静态变量 看起来相当合理 但标准也不允许goto in constexpr功能 即使它允许其他控制流机制 这种区别背后的原因是什么 我以为我们已经过去
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 是否有比 lex/flex 更好(更现代)的工具来生成 C++ 分词器?

    我最近将源文件解析添加到现有工具中 该工具从复杂的命令行参数生成输出文件 命令行参数变得如此复杂 以至于我们开始允许它们作为一个文件提供 该文件被解析为一个非常大的命令行 但语法仍然很尴尬 因此我添加了使用更合理的语法解析源文件的功能 我使
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 如何构建印度尼西亚电话号码正则表达式

    这些是一些印度尼西亚的电话号码 08xxxxxxxxx 至少包含 11 个字符长度 08xxxxxxxxxxx 始终以 08 开头 我发现这个很有用 Regex regex new Regex 08 0 9 0 9 0 9 0 9 0 9
  • GDK3/GTK3窗口更新的精确定时

    我有一个使用 GTK 用 C 语言编写的应用程序 尽管该语言对于这个问题可能并不重要 这个应用程序有全屏gtk window与单个gtk drawing area 对于绘图区域 我已经通过注册了一个刻度回调gtk widget add ti
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器
  • 不同类型的指针可以互相分配吗?

    考虑到 T1 p1 T2 p2 我们可以将 p1 分配给 p2 或反之亦然吗 如果是这样 是否可以不使用强制转换来完成 或者我们必须使用强制转换 首先 让我们考虑不进行强制转换的分配 C 2018 6 5 16 1 1 列出了简单赋值的约束

随机推荐