Int vs const int &

I noticed that I usually use constant references as return values ​​or arguments. I think the reason is that it works in much the same way as using non-links in code. But it definitely takes up more space, and function declarations get longer. I am fine with this code, but I think some people think this is a bad programming style.

What do you think? Should I write const int & over int? I think it is optimized by the compiler anyway, so maybe I just spend time coding it?

+54
c ++ reference coding-style const
Jan 16 2018-11-11T00:
source share
6 answers

In C ++, what I consider to be an anti-pattern using const T& very common as a smart way to just say T when working with parameters. However, a value and a link (whether it is constant or not) are two completely different things, and always blindly using links instead of values ​​can lead to minor errors.

The reason is that when working with links you have to consider two problems that are not in the values: lifetime and aliases .

Just as an example, one of the places where this anti-pattern is applied is the standard library itself, where std::vector<T>::push_back takes const T& instead of a value, and this can lead, for example, to code:

 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_back wants a link to const, but doing push_back may require redistribution, and if that happens, it means that after redistribution, the received link will no longer be valid (life time problem), and you will enter an area of ​​vague behavior.

Problems with aliases are also a source of subtle problems if constant references are used instead of values. For example, I was bitten by code of this type:

 struct P2d { double x, y; P2d(double x, double y) : x(x), y(y) {} P2d& operator+=(const P2d& p) { x+=px; y+=py; return *this; } P2d& operator-=(const P2d& p) { x-=px; y-=py; 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; } }; 

At first glance, the code seems pretty safe, P2d is a two-dimensional point, Rect is a rectangle, and adding / subtracting a point means translating the rectangle.

However, if you want to translate the rectangle back to the origin, write myrect -= myrect.tl; the code will not work because the translation operator was defined by accepting a link that (in this case) refers to a member of the same instance.

This means that after updating topleft with tl -= p; topleft will be (0, 0) as it should be, but also p will become (0, 0) at the same time because p is just a reference to the upper left member, and therefore updating the lower right corner will not work because it will translate it as (0, 0) therefore, doing nothing.

Please do not think that the reference to const is similar to the meaning due to the word const . This word exists only to inform you of compilation errors if you are trying to modify the reference object using this link, but does not mean that the reference object is a constant. More precisely, the object referenced by const ref may change (for example, due to aliases) and may even cease to exist while you use it (life time problem).

In const T& word const expresses a property of a link, not a reference object: it is a property that makes it impossible to use it to modify the object. Readonly would probably be a better name, since const has an IMO psychological effect on the idea that the object will be persistent while you use the link.

Of course, you can achieve impressive acceleration by using links instead of copying values, especially for large classes. But when using links, you should always think about problems with aliases and lifetimes, because undercover they simply point to other data. However, for "native" data types (integer, double, pointers), references will actually be slower than values, and when used instead of values, there is nothing to gain.

Also, a link to const will always mean problems for the optimizer, since the compiler is forced to be paranoid, and every time any unknown code is executed, it should assume that all the reference objects can now have a different value ( const means absolutely NOTHING for the optimizer for the link , this word is only to help programmers - personally, I'm not sure that this is such a big help, but this is a different story).

+130
Jan 16 2018-11-11T00:
source share

As Olya says, returning const T& in contrast to T is completely different things and can be violated in certain situations (as in his example).

Taking const T& as opposed to regular T , since the argument is less likely to break things, but still has some important differences.

  • Taking T instead of const T& requires that T be copyable.
  • Taking T will call the copy constructor, which can be expensive (as well as the destructor when the function exits).
  • Taking T allows you to change the parameter as a local variable (it can be faster than manual copying).
  • Taking const T& may be slower due to incorrect time series and indirect cost.
+13
Jan 16 '11 at 13:37
source share

int & and int not interchangeable! In particular, if you return a link to a local stack variable, the behavior is undefined, for example:

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

You can return a link to something that will not be destroyed at the end of the function (for example, static or class member). So it really is:

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

and to the outside world, it has the same effect as returning int (except that now you can change it, so you often see const int & ).

The advantage of a link is that a copy is not required, which is important if you are dealing with objects of a large class. However, in many cases, the compiler can optimize this; see for example http://en.wikipedia.org/wiki/Return_value_optimization .

+8
Jan 16 '11 at 13:30
source share

If the callee and caller are defined in separate compilation units, the compiler cannot optimize the link. 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 does not require access to main memory:

 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 eax as an argument to test1 in edi . Lines No. 2 and No. 3 push the result into the main memory and put the address in the first argument, because the argument is declared as a reference to int, and therefore it must be available, for example, accept its address. Whether something can be fully calculated using registers or to access the main memory can make a big difference these days. Thus, in addition to being more of a type, const int& can also be slower. The rule of thumb is to pass all data that does not exceed the word size by value, and everything else by reference to const. Also pass in template arguments with a reference to const; since the compiler has access to the template definition, it can always optimize the link.

+7
Jan 16 2018-11-11T00:
source share

Instead of compiler-optimized “thinking,” why don't you get an assembler list and know for sure?

junk.C ++:

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

Generated assembler output (enabled):

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

...

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

movl instructions are very different in both cases. The first moves 5 to EAX (which is the register traditionally used to return values ​​to x86 C code), and the second moves the address of the variable (specificity excluded for clarity) to EAX . This means that the calling function in the first case can simply use the register operations without clicking on the memory to use the answer, and in the second it must go into memory through the returned pointer.

So it looks like it is not optimized.

This is more than the other answers you are given here, explaining why T and const T& are not interchangeable.

+3
Jan 16 '11 at 13:50
source share

int differs from const int &:

  1. const int & is a reference to another integer variable (int B), which means: if we change int B, the value of const int & will also change.

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

See the following c ++ code:

int main () {

vector a {1,2,3};

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

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

and [2] = 111;

// b will print 3;

// c will return 111;

}

0
May 15 '19 at 4:42
source share



All Articles