gpt4 book ai didi

c++ - 为什么 C++0x 中没有编译器生成的 swap() 方法?

转载 作者:IT老高 更新时间:2023-10-28 14:00:04 26 4
gpt4 key购买 nike

C++ 编译器会自动生成复制构造函数和复制赋值运算符。为什么不也swap

如今,实现复制赋值运算符的首选方法是 copy-and-swap 习语:

T& operator=(const T& other)
{
T copy(other);
swap(copy);
return *this;
}

(ignoring the copy-elision-friendly form that uses pass-by-value)。

这个习惯用法的优点是在面对异常时是事务性的(假设 swap 实现不抛出)。相比之下,默认的编译器生成的复制赋值运算符递归地对所有基类和数据成员进行复制赋值,并且没有相同的异常安全保证。

同时,手动实现 swap 方法既繁琐又容易出错:

  1. 为确保 swap 不抛出异常,必须为类和基类中的所有非 POD 成员、它们的非 POD 成员等实现。
  2. 如果维护者向一个类添加新的数据成员,维护者必须记住修改该类的 swap 方法。不这样做可能会引入微妙的错误。此外,由于 swap 是一种普通方法,因此如果 swap 实现不完整,编译器(至少我不知道)不会发出警告。

如果编译器自动生成swap方法不是更好吗?然后隐式复制分配实现可以利用它。

显而易见的答案可能是:开发 C++ 时不存在 copy-and-swap 习语,现在这样做可能会破坏现有代码。

不过,也许人们可以选择让编译器使用 C++0x 用于控制其他隐式函数的相同语法生成 swap:

void swap() = default;

然后可能会有规则:

  1. 如果有编译器生成的 swap 方法,则可以使用 copy-and-swap 实现隐式复制赋值运算符。
  2. 如果没有编译器生成的 swap 方法,则会像以前一样实现隐式复制赋值运算符(在所有基类和所有成员上调用复制赋值)。

有谁知道 C++ 标准委员会是否曾建议过此类(疯狂的?)事情,如果是,委员会成员有何意见?

最佳答案

这是对特里的回答。

我们必须在 0x 之前在 C++ 中创建 swap 函数的原因是因为一般的自由函数 std::swap 效率较低(并且通用性较差)它可能是。它制作了一个参数的拷贝,然后进行了两次重新分配,然后释放了基本上浪费的拷贝。复制重量级类是浪费时间,因为我们作为程序员知道我们真正需要做的就是交换内部指针等等。

然而,右值引用完全解决了这个问题。在 C++0x 中,swap 实现为:

template <typename T>
void swap(T& x, T& y)
{
T temp(std::move(x));
x = std::move(y);
y = std::move(temp);
}

这更有意义。我们只是在四处移动数据,而不是复制数据。这甚至允许交换不可复制的类型,如流。 C++0x 标准草案规定,为了使用 std::swap 交换类型,它们必须是右值可构造和右值可赋值(显然)。

这个版本的 swap 基本上可以做任何自定义的书面交换函数会做的事情。考虑一个我们通常会为其编写 swap 的类(例如这个“哑” vector ):

struct dumb_vector
{
int* pi; // lots of allocated ints

// constructors, copy-constructors, move-constructors
// copy-assignment, move-assignment
};

以前,swap 会在以后丢弃之前对我们所有的数据进行冗余复制。我们自定义的 swap 函数只会交换指针,但在某些情况下使用起来可能很笨拙。在 C++0x 中,移动实现了相同的最终结果。调用 std::swap 会生成:

dumb_vector temp(std::move(x));
x = std::move(y);
y = std::move(temp);

翻译为:

dumb_vector temp;
temp.pi = x.pi; x.pi = 0; // temp(std::move(x));
x.pi = y.pi; y.pi = 0; // x = std::move(y);
y.pi = temp.pi; temp.pi = 0; // y = std::move(temp);

编译器当然会去掉多余的赋值,留下:

int* temp = x.pi;
x.pi = y.pi;
y.pi = temp;

正是我们的自定义 swap 一开始会做的。因此,虽然在 C++0x 之前我会同意您的建议,但由于引入了右值引用,自定义 swap 不再是必需的。 std::swap 可以在任何实现移动功能的类中完美运行。

事实上,我认为实现 swap 函数应该成为不好的做法。任何需要 swap 函数的类也需要右值函数。但在这种情况下,根本不需要自定义 swap 的困惑。代码大小确实增加了(两个 ravlue 函数与一个 swap),但右值引用不仅仅适用于交换,给我们留下了积极的权衡。 (总体上更快的代码,更简洁的界面,更多的代码,没有更多的 swap ADL 麻烦。)

至于我们是否可以default右值函数,我不知道。我稍后会查找它,或者也许其他人可以加入,但这肯定会有所帮助。 :)

即便如此,允许 default 右值函数而不是 swap 是有意义的。所以本质上,只要它们允许 = default 右值函数,你的请求就已经被提出了。 :)

编辑:我做了一些搜索,= default move 的提案是提案 n2583 .根据this (我不知道如何读得很好),它被“移回”了。它列在标题为“尚未为 C++0x 准备好,但可以在未来重新提交”的部分下。所以看起来它不会成为 C++0x 的一部分,但可能会在以后添加。

有点失望。 :(

编辑 2:再环顾四周,我发现:Defining Move Special Member Functions这是更新的,看起来我们可以默认 move。耶!

关于c++ - 为什么 C++0x 中没有编译器生成的 swap() 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2078515/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com