9

实施1:

foo(const Bar x);

实施2:

foo(const Bar & x);

如果对象不会在函数内更改,你为什么要复制它(实现 1)。

这会被编译器自动优化吗?

摘要:即使对象const在函数声明中被声明,仍然可以通过其他别名来编辑对象&

如果您是编写库的人并且知道您的函数不这样做,或者对象足够大以证明每次操作的取消引用成本是合理的, foo(const Bar & x);那么这就是要走的路。

第 2 部分

这会被编译器自动优化吗?

由于我们确定它们并不总是等价的,而且等价的条件很重要,编译器通常很难确保它们,所以几乎可以肯定 没有

4

4 回答 4

5

实际上,在这些情况下,您通常会使用方法 2。

通常,如果对象很小,您只会使用方法 1,因此复制一次比通过引用重复访问它更便宜(这也会产生成本)。正是出于这个原因,在TC++PL中,Stroustrup 开发了一个复数类并按值传递它。

于 2012-12-17T16:47:17.227 回答
5

你问,

“如果对象不会在函数内更改,你为什么要复制它(实现 1)。”

好吧,在某些奇怪的情况下,通过引用传递的对象可能会被其他代码更改,例如

namespace g { int x = 666; }

void bar( int ) { g::x = 0; }

int foo( int const& a ) { assert( a != 0 );  bar( a );  return 1000/a; }  // Oops

int main() { foo( g::x ); }

但自 1990 年代中期以来,这从未发生在我身上。

因此,这种别名是该类型的单个参数的理论问题

使用相同类型的两个参数,它获得了更多真正的可能性。例如,赋值运算符可能会传递调用它的对象。当参数按值传递时(如交换习语的最小形式),没问题,但如果不是,则通常需要避免自赋值。

你进一步问,

“这会被编译器自动优化吗?”

不,一般来说,由于上述原因

编译器通常不能保证引用参数不会有别名(但是,一个例外是调用的机器代码被内联的地方)

然而,另一方面,可以想象,该语言可以在这方面支持编译器,例如,通过为程序员提供一种明确接受任何此类优化的方法,例如,一种方式可以说“通过替换 pass by 来优化此代码是安全的通过引用传递的值,编译器,请随心所欲”

于 2012-12-17T17:01:25.633 回答
2

在某些情况下它可能会被优化,但有很多事情可以阻止它。如果出现以下情况,编译器将无法避免复制:

  • 复制构造函数或析构函数有副作用,传递的参数不是临时的。
  • 您获取 的地址x或对它的引用,并将其传递给一些可能能够将其与原始地址进行比较的代码。
  • 对象在foo运行时可能会发生变化,例如因为foo调用了其他一些改变它的函数。我不确定这是否是您要通过说“对象不会在函数内更改”来排除的事情,但如果不是,那么它就在起作用。

如果这些事情对你的程序很重要,你会复制它:

  • 如果您想要复制的副作用,请复制
  • 如果您希望“您的”对象与用户提供的参数具有不同的地址,请复制一份
  • 如果您不想在函数运行期间看到对原始文件所做的更改,请复制一份

如果您认为副本会更有效,您也可以复制它,这通常被认为是“小”类型的情况,例如int. 标准算法中的迭代器和谓词也按值取值。

最后,如果您的代码无论如何都计划复制对象(包括通过分配给现有对象),那么一个合理的习惯用法是首先将副本作为参数。然后从您的参数移动/交换。

于 2012-12-17T16:57:00.947 回答
1

如果对象是从其他地方更改的怎么办?

void f(const SomeType& s);
void g(const SomeType s);

int main() {
    SomeType s;

    std::thread([&](){ /* s is non-const here, and we can modify it */}

    // we get a const reference to the object which we see as const, 
    // but others might not. So they can modify it.
    f(s);

    // we get a const *copy* of the object, 
    // so what anyone else might do to the original doesn't matter 
    g(s);
}

如果对象是 const,但具有可变成员怎么办?然后您仍然可以修改对象,因此您是否拥有对原始对象的副本或引用非常重要。

如果对象包含指向另一个对象的指针怎么办?如果s是 const,则指针将是 const,但它指向的内容不受 const 的影响s。但是创建一个副本将(希望)给我们一个深拷贝,所以我们得到了我们自己的(const)对象和一个单独的(const)指针,指向一个单独的(non-const)对象。

在许多情况下,const 副本与 const 引用不同。

于 2012-12-17T17:13:59.597 回答