简体   繁体   English

传递(int x)和(const int&x)之间的区别

[英]Difference between passing (int x) and (const int &x)

I'm reading the book C++ Primer Plus (6th Edition) and I've come across something that is kind of confusing to me, so bear with me while I try to explain... 我正在阅读C ++ Primer Plus(第6版)这本书,我遇到了一些让我感到困惑的事情,所以在我试图解释时请耐心等待......

If I have a function whose prototype looks like so: 如果我有一个原型如此的函数:

void cube(double x);

and another function who prototype looks like so: 和原型的另一个功能看起来像这样:

void cube(const double &x);

what is the difference between the two? 两者有什么区别? For the first function prototype, the value is passed by value meaning that it will be copied and thus unaltered by the function. 对于第一个函数原型,值通过值传递,这意味着它将被复制,因此不会被函数更改。 For the second prototype, the value is passed by reference but it's a constant reference so C++ will create an anonymous temporary variable and assign the value of the argument to the temporary variable thus mimicking pass by value. 对于第二个原型,值通过引用传递,但它是一个常量引用,因此C ++将创建一个匿名临时变量,并将参数的值赋给临时变量,从而模仿pass by value。 So, in essence there is really no difference between the two function prototypes, right? 所以,实质上两个函数原型之间确实没有区别,对吧? What is the point of (const double &x) then? 那么(const double&x)有什么意义呢?

For the second prototype, the value is passed by reference but it's a constant reference so C++ will create an anonymous temporary variable and assign the value of the argument to the temporary variable thus mimicking pass by value. 对于第二个原型,值通过引用传递,但它是一个常量引用,因此C ++将创建一个匿名临时变量,并将参数的值赋给临时变量,从而模仿pass by value。

Although C++ would do this in certain situations. 虽然C ++会在某些情况下执行此操作。 For example, if you pass an expression returning double , C++ will create a temporary: 例如,如果传递一个返回double的表达式,C ++将创建一个临时的:

double v = 123.456;
cube(5*v+321.0123);

However, it would not necessarily do that. 但是,它不一定会那样做。 For example, if you make this call 例如,如果您拨打此电话

double v = 123.456;
cube(v);

C++ would pass a reference to v directly to the cube() function. C ++会直接将对v的引用传递给cube()函数。 In this case, concurrent modifications to v would be "visible" to the cube() function while it is running. 在这种情况下,对v并发修改在运行时对cube()函数是“可见的”。

So, in essence there is really no difference between the two function prototypes, right? 所以,实质上两个函数原型之间确实没有区别,对吧?

That's right, there isn't much difference between the two. 没错,两者之间差别不大。 Assuming that double takes the same amount of space as a reference to double , there would be no difference in performance as well. 假设double占用的空间量与double的引用量相同,那么性能也没有差别。

What is the point of (const double &x) then? 那么(const double &x)什么意义呢?

Although there is little difference between passing double by value or by constant reference, you may get considerable difference when dealing with other types, such as std::string . 尽管传递double by值或通过常量引用之间几乎没有区别,但在处理其他类型(例如std::string时可能会有相当大的差异。 Taking parameters by constant reference becomes very useful when you code an algorithm as a template, ie when you have to write 当您将算法编码为模板时,即必须编写时,通过常量引用获取参数会非常有用

void cube(const T& v);

and your T could be any type. 你的T可以是任何类型。 Constant reference approach lets you control the amount of copying that is going on, because starting at a certain object size, passing a reference becomes much cheaper than passing a copy. 常量引用方法允许您控制正在进行的复制量,因为从特定对象大小开始,传递引用变得比传递副本便宜得多。

For the following two prototypes 对于以下两个原型

void cube(double x);

and

void cube(const double &x);

the behavior will be the same from the perspective of the caller and the callee in that neither will allow modification to propagate to the caller. 从调用者和被调用者的角度来看,行为将是相同的,因为它们都不允许修改传播给调用者。 However, in the latter (const reference) example, x will be passed by reference instead of by value. 但是,在后者(const引用)示例中,x将通过引用而不是值传递。 If double were large enough, or if it was a larger data type (eg, struct) passing by reference would avoid copying a lot of data (since only the pointer is passed). 如果double足够大,或者它是一个更大的数据类型(例如,struct),则通过引用传递将避免复制大量数据(因为只传递指针)。

The second-order performance effects (eg, effects on cache weighed against the copy of the data) are all very implementation-dependent, and depend not only upon the processor / cache architecture and compiler, but on the structure and runtime use case of the program. 二阶性能影响(例如,对数据副本权衡的缓存影响)都是非常依赖于实现的,并且不仅取决于处理器/缓存架构和编译器,还取决于结构和运行时用例。程序。

For primitive types, you don't gain anything by using a const& . 对于原始类型,使用const&不会获得任何东西。 const& is traditionally used for big objects to avoid possibly expensive and unnecessary copying. const&传统上用于大对象,以避免可能昂贵和不必要的复制。

You also need const& if the type does not support copying yet you want to disallow modification of the referenced object. 你还需要const&如果类型不支持复制,你想要禁止修改引用的对象。

With the advent of C++11, move semantics question the traditional wisdom of "pass small objects by value, big objects by const reference" As far as I can see, the C++ expert community has not yet reached a new consensus on this topic. 随着C ++ 11的出现, 移动语义质疑“通过const传递小对象,通过const引用传递大对象”的传统智慧据我所知,C ++专家社区尚未就此主题达成新的共识。 For example, see How true is "Want Speed? Pass by value" . 例如,请参阅“想要速度?按值传递”是多么真实

Well, there is some difference: 嗯,有一些区别:

  • With void cube(double x) , you're making a local copy of variable x in your function, and you can change its value without changing the value of the original x. 使用void cube(double x) ,您可以在函数中创建变量x的本地副本 ,并且可以在不更改原始x的值的情况下更改其值。 So, in the local function void cube , you can interact with x as with ordinal variable. 因此,在本地函数void cube ,您可以像使用序数变量一样与x进行交互。
  • With void cube(const double& x) , you're just passing a pointer (in c++, reference is a pointer but with slightly another syntax of usage), and const here means that you can't change the value of the variable in this address (that the pointer is pointed on). 使用void cube(const double& x) ,你只是传递一个指针 (在c ++中,引用是一个指针,但略带另一种使用语法),而这里的const意味着你不能在这里改变变量的值地址(指针指向)。 So, in the local function void cube , you should interact with x as with a constant variable. 因此,在本地函数void cube ,您应该像使用常量变量一样与x进行交互。

What about difference in performance? 性能差异怎么样?

With double there's no difference in performance, because double takes 64 bits, and the reference to double takes 32 or 64 bits, not much difference. 使用double ,性能没有差别,因为double需要64位,而double的引用需要32或64位,差别不大。 But, imagine you have a struct: 但是,假设你有一个结构:

struct some_very_big_struct {
    ...
}

where sizeof(some_very_big_struct) is 2^10 bytes, so making a copy of this struct takes really much time and additional memory, so in that case passing a reference is the best choice. 其中sizeof(some_very_big_struct)2^10个字节,因此制作此结构的副本需要花费很多时间和额外的内存,因此在这种情况下传递引用是最佳选择。

The standard clearly requires, subject to the rules in [dcl.init.ref] #5.1 , that the reference parameter to be bound to the argument, as opposed (just for example) to the reference to be bound to some copy. 根据[dcl.init.ref] #5.1的规则,该标准明确要求将参数绑定到参数,而不是(仅作为示例)绑定到某个副本的引用。 The difference is detectable, so no "as if" rule is applicable (in all cases). 差异是可检测的,因此没有“似乎”规则适用(在所有情况下)。

double a;
void foo(const double &x) { assert(&a == &x); }
void bar() { foo(a); }

There is a practical difference too. 还有一个实际的区别。 In the case of const double &x , the name x is just an alias for some double that may be accessed via other lvalues, which has a lot of implications to optimisation (like register saves/restores, instruction reordering, etc), for example: const double &x的情况下,名称x只是某些double的别名,可以通过其他左值访问,这对优化有很多影响(如寄存器保存/恢复,指令重新排序等),例如:

  double a;
  void foo(const double &x) {
       ... = x;
       a = bar(); // here the compiler must assume that the assignment
                  // may modify x, the two statements can't be reordered
  }

  void foo(double x) {
       ... = x;
       a = bar(); // here the compiler knows that the assignment
                  // cannot modify x, and the two statements can be
                  // reordered
  }

Although the read/write ops would slow it down, a reference/pointer would only be 32 bits on 32bit machine, where a double would be 64 bits. 虽然读/写操作会降低它的速度,但参考/指针在32位机器上只有32位,其中double是64位。 That doesn't play as well with a 32 bit word size. 对于32位字大小,这不会发挥得那么好。

It makes no sense, really. 真的,没有任何意义。 An optimising compiler will probably treat them the same and generate identical code. 优化编译器可能会对它们进行相同的处理并生成相同的代码。

EDIT: To explain myself, I should have emphasised "probably". 编辑:为了解释自己,我应该强调“可能”。

Strict standards compliance is currently a bad way to judge whether or not a compiler is indeed a C++ compiler - compilers often abuse/ignore the standard in favour of optimisations and syntactic sugar. 严格的标准合规性目前是判断编译器是否确实是C ++编译器的一种坏方法 - 编译器经常滥用/忽略标准而支持优化和语法糖。

As for the (performance) difference, I was trying to say measuring the cost of passing a double (over the word size) to the function VS the cost of fetching from a pointer/reference (word size) - and in most cases it's negligible. 至于(性能)差异,我试图说测量将双倍(超过字大小)传递给函数VS从指针/参考(字大小)获取的成本 - 并且在大多数情况下它可以忽略不计。 The larger the value you're passing, the faster fetching from pointer/reference will become. 您传递的值越大,从指针/引用获取的速度就越快。
Casey Muratori mentioned this, and encourages passing structs slightly over the word size. Casey Muratori提到了这一点,并鼓励在字大小上略微传递结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM