C++ int vs const int&

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4705593/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:11:54  来源:igfitidea点击:

int vs const int&

c++coding-stylereferenceconst

提问by Pijusn

I've noticed that I usually use constant references as return values or arguments. I think the reason is that it works almost the same as using non-reference in the code. But it definitely takes more space and function declarations become longer. I'm OK with such code but I think some people my find it a bad programming style.

我注意到我通常使用常量引用作为返回值或参数。我认为原因是它的工作原理与在代码中使用非引用几乎相同。但它肯定需要更多空间并且函数声明变得更长。我可以接受这样的代码,但我认为有些人认为这是一种糟糕的编程风格。

What do you think? Is it worth writing const int&over int? I think it's optimized by the compiler anyway, so maybe I'm just wasting my time coding it, a?

你怎么认为?值得在int 上const int&吗?我认为它无论如何都是由编译器优化的,所以也许我只是在浪费时间编码它,一个?

回答by 6502

In C++ it's very common what I consider an anti-pattern that uses const T&like a smart way of just saying Twhen dealing with parameters. However a value and a reference (no matter if const or not) are two completely different things and always and blindly using references instead of values can lead to subtle bugs.

在 C++ 中,我认为一种反模式很常见,它在处理参数时使用const T&就像一种聪明的方式T。然而,值和引用(无论是否为常量)是两个完全不同的东西,总是盲目地使用引用而不是值会导致微妙的错误。

The reason is that when dealing with references you must consider two issues that are not present with values: lifetimeand aliasing.

原因是在处理引用时,您必须考虑值不存在的两个问题:生命周期别名

Just as an example one place where this anti-pattern is applied is the standard library itself, where std::vector<T>::push_backaccepts as parameter a const T&instead of a value and this can bite back for example in code like:

作为一个例子,应用这种反模式的地方是标准库本身,其中std::vector<T>::push_back接受作为参数 aconst T&而不是值,这可以在例如以下代码中反咬一口:

std::vector<T> v;
...
if (v.size())
    v.push_back(v[0]); // Add first element also as last element

This code is a ticking bomb because std::vector::push_backwants a const reference but doing the push_back may require a reallocation and if that happens means that after the reallocation the reference received would not be valid any more (lifetimeissue) and you enter the Undefined Behaviour realm.

这段代码是一个定时炸弹,因为std::vector::push_back想要一个 const 引用,但执行 push_back 可能需要重新分配,如果发生这种情况,则意味着在重新分配后,收到的引用将不再有效(生命周期问题),您将进入未定义行为领域。

Aliasing issues are also a source of subtle problems if const references are used instead of values. I've been bitten for example by code of this kind:

如果使用 const 引用而不是值,则别名问题也是微妙问题的根源。例如,我被这种代码咬过:

struct P2d
{ 
    double x, y;
    P2d(double x, double y) : x(x), y(y) {}
    P2d& operator+=(const P2d& p) { x+=p.x; y+=p.y; return *this; }
    P2d& operator-=(const P2d& p) { x-=p.x; y-=p.y; return *this; }
};

struct Rect
{
    P2d tl, br;
    Rect(const P2d& tl, const P2d& br) : tl(tl), bt(br) {}
    Rect& operator+=(const P2d& p) { tl+=p; br+=p; return *this; }
    Rect& operator-=(const P2d& p) { tl-=p; br-=p; return *this; }
};

The code seems at a first glance pretty safe, P2dis a bidimensional point, Rectis a rectangle and adding/subtracting a point means translating the rectangle.

代码乍一看很安全,P2d是一个二维点,Rect是一个矩形,添加/减去一个点意味着平移矩形。

If however to translate the rectangle back in the origin you write myrect -= myrect.tl;the code will not work because the translation operator has been defined accepting a reference that (in that case) is referencing a member of same instance.

但是,如果要将矩形转换回原点,您编写myrect -= myrect.tl;的代码将不起作用,因为转换运算符已被定义为接受(在这种情况下)引用同一实例的成员的引用。

This means that after updating the topleft with tl -= p;the topleft will be (0, 0)as it should but also pwill become at the same time (0, 0)because pis just a reference to the top-left member and so the update of bottom-right corner will not work because it will translate it by (0, 0)hence doing basically nothing.

这意味着在用tl -= p;topleft更新topleft 后将是(0, 0)它应该的,但也会同时p变为(0, 0)因为p它只是对左上角成员的引用,因此右下角的更新将不起作用,因为它会平移因此,它(0, 0)基本上什么都不做。

Please don't be fooled into thinking that a const reference is like a value because of the word const. That word exists only to give you compile errors if you try to change the referenced object using that reference, but doesn't mean that the referenced object is constant. More specifically the object referenced by a const ref can change (e.g. because of aliasing) and can even get out of existence while you are using it (lifetimeissue).

请不要因为字而误以为常量引用就像一个值const。如果您尝试使用该 reference更改引用的对象该词的存在只是为了给您编译错误,但并不意味着被引用的对象是常量。更具体地说,const ref 引用的对象可能会更改(例如,由于别名),甚至在您使用它时可能会消失(生命周期问题)。

In const T&the word constexpresses a property of the reference, not of the referenced object: it's the property that makes impossible to use it to change the object. Probably readonlywould have been a better name as consthas IMO the psychological effect of pushing the idea that the object is going to be constant while you use the reference.

const T&单词const 中,表示引用的属性,而不是被引用对象的属性:正是该属性无法使用它来更改对象。可能readonly会是一个更好的名称,因为const具有 IMO 的心理影响,即在您使用引用时推动对象将保持不变的想法。

You can of course get impressive speedups by using references instead of copying the values, especially for big classes. But you should always think about aliasing and lifetime issues when using references because under the cover they're just pointers to other data. For "native" data types (ints, doubles, pointers) references however are actually going to be slower than values and there's nothing to gain in using them instead of values.

您当然可以通过使用引用而不是复制值来获得令人印象深刻的加速,尤其是对于大类。但是在使用引用时,您应该始终考虑别名和生命周期问题,因为实际上它们只是指向其他数据的指针。然而,对于“原生”数据类型(整数、双精度、指针),引用实际上会比值慢,并且使用它们代替值没有任何好处。

Also a const reference will always mean problems for the optimizer as the compiler is forced to be paranoid and every time any unknown code is executed it must assume that all referenced objects may have now a different value (constfor a reference means absolutely NOTHING for the optimizer; that word is there only to help programmers - I'm personally not so sure it's such a big help, but that's another story).

此外,const 引用始终意味着优化器的问题,因为编译器被迫偏执,并且每次执行任何未知代码时,它都必须假设所有引用的对象现在可能具有不同的值(const引用意味着优化器绝对没有; 这个词只是为了帮助程序员 - 我个人不太确定它有这么大的帮助,但那是另一个故事)。

回答by Peter Alexander

As Oli says, returning a const T&as opposed to Tare completely different things, and may break in certain situations (as in his example).

正如 Oli 所说,返回 aconst T&而不是T完全不同的东西,并且在某些情况下可能会中断(如他的示例)。

Taking const T&as opposed to plain Tas an argument is less likely to break things, but still have several important differences.

const T&与普通的相反T作为一个论点不太可能破坏事情,但仍然有几个重要的区别。

  • Taking Tinstead of const T&requires that Tis copy-constructible.
  • Taking Twill invoke the copy constructor, which may be expensive (and also the destructor on function exit).
  • Taking Tallows you to modify the parameter as a local variable (can be faster than manually copying).
  • Taking const T&could be slower due to misaligned temporaries and the cost of indirection.
  • T代替const T&要求T是拷贝构造。
  • TakeT将调用复制构造函数,这可能很昂贵(还有函数退出时的析构函数)。
  • TakeT允许您将参数修改为局部变量(可以比手动复制更快)。
  • 考虑const T&可能会比较慢,由于错位的临时和间接成本。

回答by Oliver Charlesworth

int &and intare not interchangeable! In particular, if you return a reference to a local stack variable, the behaviour is undefined, e.g.:

int &并且int不可互换!特别是,如果您返回对局部堆栈变量的引用,则行为未定义,例如:

int &func()
{
    int x = 42;
    return x;
}

You canreturn a reference to something that won't be destroyed at the end of the function (e.g. a static, or a class member). So this is valid:

可以返回对在函数结束时不会被销毁的内容的引用(例如静态或类成员)。所以这是有效的:

int &func()
{
    static int x = 42;
    return x;
}

and to the outside world, has the same effect as returning the intdirectly (except that you can now modify it, which is why you see const int &a lot).

和对外界,和int直接返回一样的效果(除了你现在可以修改它,这就是你看到const int &很多的原因)。

The advantage of the reference is that no copy is required, which is important if you're dealing with large class objects. However, in many cases, the compiler can optimize that away; see e.g. http://en.wikipedia.org/wiki/Return_value_optimization.

引用的优点是不需要复制,这在处理大型类对象时很重要。但是,在许多情况下,编译器可以将其优化掉;参见例如http://en.wikipedia.org/wiki/Return_value_optimization

回答by Philipp

If the callee and the caller are defined in separate compilation units, then the compiler cannot optimize away the reference. For example, I compiled the following code:

如果被调用者和调用者是在不同的编译单元中定义的,那么编译器就不能优化掉引用。例如,我编译了以下代码:

#include <ctime>
#include <iostream>

int test1(int i);
int test2(const int& i);

int main() {
  int i = std::time(0);
  int j = test1(i);
  int k = test2(i);
  std::cout << j + k << std::endl;
}

with G++ on 64-bit Linux at optimization level 3. The first call needs no access to main memory:

在优化级别为 3 的 64 位 Linux 上使用 G++。第一次调用不需要访问主内存:

call    time
movl    %eax, %edi     #1
movl    %eax, 12(%rsp) #2
call    _Z5test1i
leaq    12(%rsp), %rdi #3
movl    %eax, %ebx
call    _Z5test2RKi

Line #1 directly uses the return value in eaxas argument for test1in edi. Line #2 and #3 push the result into main memory and place the address in the first argument because the argument is declared as reference to int, and so it must be possible to e.g. take its address. Whether something can be calculated entirely using registers or needs to access main memory can make a great difference these days. So, apart from being more to type, const int&can also be slower. The rule of thumb is, pass all data that is at most as large as the word size by value, and everything else by reference to const. Also pass templated arguments by reference to const; since the compiler has access to the definition of the template, it can always optimize the reference away.

1号线直接使用返回值eax作为参数进行test1edi。第 2 行和第 3 行将结果推入主内存并将地址放在第一个参数中,因为该参数被声明为对 int 的引用,因此必须可以例如获取其地址。如今,是否可以完全使用寄存器计算某些内容或需要访问主内存会产生很大的不同。所以,除了打字更多,const int&还可以更慢。经验法则是,通过值传递最多与字大小一样大的所有数据,并通过引用 const 传递其他所有数据。还通过引用 const 传递模板化参数;由于编译器可以访问模板的定义,因此它始终可以优化引用。

回答by JUST MY correct OPINION

Instead of "thinking" it's optimized away by the compiler, why don't you get the assembler listing and find out for sure?

与其“思考”它已被编译器优化掉,为什么不获取汇编程序列表并确定一下呢?

junk.c++:

垃圾.c++:

int my_int()
{
    static int v = 5;
    return v;
}

const int& my_int_ref()
{
    static int v = 5;
    return v;
}

Generated assembler output (elided):

生成的汇编器输出(省略):

_Z6my_intv:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    movl    , %eax
    ret
    .cfi_endproc

...

...

_Z10my_int_refv:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    movl    $_ZZ10my_int_refvE1v, %eax
    ret

The movlinstructions in both are very different. The first moves 5into EAX(which happens to be the register traditionally used to return values in x86 C code) and the second moves the address of a variable (specifics elided for clarity) into EAX. That means the calling function in the first case can just directly use register operations without hitting memory to use the answer while in the second it has to hit memory through the returned pointer.

movl两者中的说明非常不同。第一个5移入EAX(恰好是传统上用于在 x86 C 代码中返回值的寄存器),第二个移入EAX. 这意味着第一种情况下的调用函数可以直接使用寄存器操作,而无需访问内存来使用答案,而在第二种情况下,它必须通过返回的指针访问内存。

So it looks like it's not optimized away.

所以看起来它没有被优化掉。

This is over and above the other answers you've been given here explaining why Tand const T&are not interchangeable.

这超出了您在此处给出的其他答案,这些答案解释了为什么T并且const T&不可互换。

回答by Zhihui Shao

int is differentwith const int&:

int与 const int&不同

  1. const int& is the reference to another integer variable (int B), which means: if we change int B, the value of const int& will also change.
  1. const int& 是对另一个整型变量(int B)的引用,这意味着:如果我们改变 int B,那么 const int& 的值也会改变。

2, int is the value copy of another integer variable (int B), which means: if we change int B, the value of int will not change.

2、int是另一个整型变量(int B)的值拷贝,意思是:如果我们改变int B,int的值不会改变。

See the following c++ code:

请参阅以下 C++ 代码:

int main(){

int main(){

vector a{1,2,3};

向量 a{1,2,3};

int b = a[2];//the value not change even when vector change

int b = a[2];//即使向量改变,值也不改变

const int& c = a[2];//this is reference, so the value depend on vector;

const int& c = a[2];//这是引用,所以值取决于向量;

a[2]=111;

[2]=111;

// b will output 3;

// b 将输出 3;

// c will output 111;

// c 将输出 111;

}

}