使用参考有效交换?

时间:2019-04-09 21:57:02

标签: c++

我一直在阅读移动语义,我相信接下来会是一个非常有效的交换(为了方便起见,我们现在将所有内容保持为整型,而不是使用泛型):

void swap(int& a, int& b) { 
    int tmp = std::move(a);
    a = std::move(b); 
    b = std::move(tmp);
}

这是正确的交换实现(对于int),对吗?第2行不会在tmp的RAM中创建额外的项,对吧?

我的另一个问题是,是否使用引用而不是std :: move来执行相同的代码,就像上面的“移动语义”示例一样,它的工作也一样好(例如,没有为tmp创建额外的变量) ?

void swap(int& a, int& b) { 
    int tmp = &a;
    a = &b; 
    b = &tmp;
}

以上代码是否为tmp创建了一个额外的变量?是否正确交换了ab

2 个答案:

答案 0 :(得分:4)

内置类型没有移动构造函数或移动赋值运算符,因此您的第一个版本确实与

没什么不同
void swap(int& a, int& b)
{ 
    int tmp = a;
    a = b; 
    b = tmp;
}

您的第二个版本won't compile。但是,最有可能的是,无论如何,您只是想编写与我的版本相同的东西。两种版本都引入了一个名为tmp的临时变量,以保留需要分配给一个交换对象的值,而另一个交换对象的值已更改。

不要在假设代码中的每个构造(例如变量)在生成的机器代码中恰好具有一个相应的构造(例如“ RAM中的多余项”)的情况下始终会运行。不是现代编译器的工作方式。编译器的工作不是获取每一行源代码,而是以特定的机器指令序列执行1:1替换。编译器的工作是采用您用C ++语言描述的整个程序,并以例如机器码的形式生成一个equivalent程序,该程序在执行时将以与程序无关的方式运行。 C ++源代码描述的程序的行为。

如果您生成的take a look at the assembly,您会看到,在交换普通整数的情况下,没有理智的编译器会将此临时变量移至内存,而是使用寄存器交换值。您可以相信您的编译器知道在给定的目标体系结构上交换两个整数的最有效方法是什么。如果您不相信编译器知道这一点,那么您应该在寻找更好的编译器……

实际上,仅通过查看隔离生成的代码,很难说出这个swap函数的“效率”如何。实际上,通常应该完全优化掉上面的swap之类的功能。它通常会留在机器代码中的唯一轨迹是它对数据流(example here)的影响,但是永远不会有实际的调用指令来调用单独的机器代码来进行交换。在优化swap时,要做的真正重要的事情是确保编译器 实际上可以对其进行优化(通常归结为确保无论在何处使用它都是已知的。

故事的寓意:不着重于描述您认为机器代码应该如何工作,而是着重于以您和编译器都可读的方式表达您的意图,并且首先要以一种< em>正确根据语言规则(而不是目标硬件的规则)描述预期的行为。切勿在计算机级别上依赖于您认为的某种语言构造映射。依靠C ++语言关于特定语言构造所引起的行为的说法。现代C ++编译器非常擅长将复杂的C ++代码转换为精简而高效的机器代码,可以精确地完成C ++中的表达。但是,他们这样做是通过积极利用这样一个事实,即所表达的行为也是唯一必须遵守的行为。结果,现代的C ++编译器在生成不是编写的代码而只是在编写时就想到的代码时,非常糟糕

答案 1 :(得分:2)

使用引用交换看起来像这样:

void swap(int& a, int& b) {
    int tmp = a;
    a = b;
    b = tmp;
}

仅在声明&为参考时才使用,而在使用时则不然。引用可以像常规变量一样使用。

因为您使用的是整数,所以此交换的效率与与std::move的交换一样有效。仅当您使用拥有资源的类时,移动语义才有好处。

例如,向量拥有指向数组的指针,因此移动向量比复制向量要有效得多,因为复制向量时,您必须复制向量内部数组中的所有数据。另一方面,如果移动向量,则不必复制内部数组。只需将指针复制到数组即可,而且快得多。