2

When you do this:

int square(int& x) { return x*x;};

int s = square(40); // This gives error

To fix this you do this:

int square(const int& x) { return x*x;};

int s = square(40); // This is OK

I understand that 40 is a constant but so what if I do this:

const int& x = 40 

Why would that be okay only with const keyword? Is that the way the compiler protect that no one can change the value referred to by x?

40 is a const so we don't even know its location in memory, but shouldn't the compiler know this address, therefore changing the value from 40 to 30 for example should be allowed since the compiler can just go to address &40 and change the value to 30?

David G
  • 94,763
  • 41
  • 167
  • 253
Kam
  • 5,878
  • 10
  • 53
  • 97

5 Answers5

2

Just because it's possible to implement doesn't mean you should do it. Having 40 really be 30 is hilarious but especially unmaintainable and should not be permitted. In addition, 40 doesn't necessarily actually have an address. Consider a 40 in cache, register, or an immediate instruction.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Actually, I heard that some versions of FORTRAN put even literals in a constants pool; the fun fact is that they could get accidentally modified when passed to functions, since they have pass by reference by default. Happy debugging! :) – Matteo Italia Nov 11 '12 at 14:50
  • @Kam it does not force the compiler to do anything. It will aggressively remove unused values when you turn optimizations on. But `const int& x = 40;` has the same semantics as `int = 40;const int& x = ;` But that has no bearing on what the compiler actually implements because of the `as is` rule that allows the compiler to remove all junk. – Martin York Nov 11 '12 at 15:06
2

The declaration of square:

 int square(int& x);

takes a lvalue, while invocation of square(40) takes a rvalue, this is inconsistent, see more of lvalue and rvalue here.

Community
  • 1
  • 1
Zhi Wang
  • 1,138
  • 4
  • 20
  • 34
2

The rules about references is that you cannot bind a temporary to non-const lvalue reference. The literal 40 can be used to initialize a temporary but it is, itself, not an object. Thus, you cannot bind a non-const lvalue reference to it.

When you do

int const& value = 40;

you actually bind a temporary object initialized to be 40 to the reference value. Normally, temporaries go out of scope and are destroyed at the end of a full-expression. However, when you directly bind a temporary to a non-const reference its life-time is extended to match the life-time of the reference.

The rules prohibiting binding of temporary objects to non-const references is in place primarily because it would probably cause many surprising results. It could technically be done but would be quite likely to produce non-obvious results.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • @Kam: Cache is a separate issue. The temporary that x is referencing would have to exist for the lifetime of the reference. If the reference was a global variable, then yes the temporary would need to exist until the process exits, but the hardware is free to move memory in and out of cache as it wants. – Vaughn Cato Nov 11 '12 at 15:05
  • @Kam: The literal `40` almost certainly lives in some read-only memory, possibly in the assembler (for small values there are typically instructions which can set a register or address; for bigger values it would be living somewhere as constant). When you create a `const` reference to it, a temporary is constructed where the compiler sees fit to keep temporaries (probably on the stack). Whether this lives in a cache is up to the system. If the address of this temporary is never taken, it may actually not even be created: All rules in the C++ standard are governed by the "as if"-rule. – Dietmar Kühl Nov 11 '12 at 15:05
1

You're confusing constants and literals. They are similar, but not equivalent. 40 is not a constant, it's a literal.

You can't pass a literal by reference, since if you pass something by reference, it means it can be modified - literals cannot. Consider the following:

void foo(int &i)
{
    i = 1;
}

foo(0); // What on Earth? 0 == 1?

If you, however, pass a reference to a constant, it means that even if it's a reference, the function is not permitted to modify its argument (since it's a constant), so now you can safely pass in a literal - it now makes sense, since there's no possibility for the function modifying its argument.

1

You can still do:

int x = 40;
int s = square(x)

x = 30;
s = square(x);

with both versions of square (the one or without const).

When you pass something by reference you are passing an existing object (because that is what a reference means an alias to an existing object).

In your example:

int s = square(30);

You are NOT passing an object. This is a literal (they are not objects). The compiler can convert literals into object by creating a temporary object. But the language has explicit restrictions on temporary object that mean they are const. This means references can not be passed to an interface where they will be mutated (though you can pass them by const reference).

Martin York
  • 257,169
  • 86
  • 333
  • 562