在什么情况下,运算符=应该用左值/右值重载而不是复制和交换来实现

Under what situations should operator= be implemented with lvalue/rvalue overloads instead of with copy-and-swap?

本文关键字:重载 复制 实现 交换 运算符 情况下 在什么      更新时间:2023-10-16

正如 https://stackoverflow.com/a/3279550/943619 所描述的那样,实现operator=的正确方法通常是:

my_type& operator=(my_type other) {
    swap(*this, other);
    return *this;
}
friend void swap(my_type& lhs, my_type& rhs) { ... }  // This can vary some.

然而,Howard Hinnant 指出,通过为左值和右值参数实现单独的重载,有时可以做得更好:

my_type& operator=(const my_type& other) { ... }
my_type& operator=(my_type&& other) { ... }

显然,2-过载解决方案可以在几种情况下节省一次移动,但如此小的改进不太可能出现在基准测试中。相反,我正在寻找编写 2 个重载可以 ~使性能加倍的情况。

"复制和交换"成语的主要问题是效率较低。

即使我们有一个很好的swap实现,我们仍然同时有 3 个类实例,而左值和右值运算符的另一个解决方案只有 2 个实例。

这意味着复制和交换所需的内存增加了 50%,并且还可能产生大量时间成本 - 因为您必须获取原本不需要的资源。

考虑std::vector情况:如果您执行vec1=vec2;并且两者都具有相同的大小,请使用左值引用 - 您根本不需要执行任何newdelete。在复制和交换中,您必须创建第三个向量 - 因此您有一个额外的newdelete,可能需要大量时间(如果向量很小,则很重要)或大量内存(如果向量很大)

复制和交换的主要优点是异常安全。 复制和交换为您提供了强大的异常安全性,但代价是性能。

如果您需要异常安全 - 使用它。如果您需要性能(内存、时间、类获取的任何其他资源),请不要使用它。