casting a parameter as a reference

May 31, 2012 at 9:13pm
Hi all,

I usually write code in C but I'm currently doing some C++ and came across something along the lines of the code below. I don't fully understand C++ references so my question is what is C++ doing when it casts a parameter as a type reference? This code compiles but does not update A.

I appreciate any help.

void myFunction(short &A)
{
A=10;
}

void main()
{
int A=0;
myFunction((short&)A);
printf("A=%d\r\n",A); /* A=0 will be the output */
}
Last edited on May 31, 2012 at 9:39pm
May 31, 2012 at 9:24pm
You should consider the cast an error. It probably don't work because of that bad cast.
May 31, 2012 at 9:24pm
I wrote the equivalent program, and it worked correctly. I used g++ to build it.

The reference acts similar to a pointer (actually, it's probably implemented as a pointer under the hood). So, the expectation is that A should be changed during the function call.

What may be happening in your situation is because A in main() is an int and the argument to myFunction is a reference to a short, the compiler might have created a temporary short before passing it to the function. That's just a guess.

One note. void main() is not legal C++. Many compilers are lazy and allow it, and many books promote its use. But it is illegal. Legal C++ requires that you use int main().

Also, when posting code, please use the code tags. They show up as "<>" in the Format box on the right.
May 31, 2012 at 9:28pm
The cast to short& is to satisfy the function argument of a reference to a short int. This code does update A.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

void myFunction(short &A)
{
  A=10;
}

int main() // main() in C++ always returns int
{
  int A=0;
  myFunction((short&)A);
  std::cout << A << std::endl;
  return 0;
}


Output is: 10
Last edited on May 31, 2012 at 9:30pm
May 31, 2012 at 9:28pm
According to the C++ Standard "The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type"

So using this expression (short&)A you get the reference to A which will be interpretated as short.
On the other hand using the expression (short)A will not allow to compile the statement

myFunction( (short)A);


because in this case a temporary unnamed object will be created which can be binded only with a const reference.
Last edited on May 31, 2012 at 9:28pm
May 31, 2012 at 9:28pm
A reference is an alias, a second (third, fourth, etc) name of an object that already was given a name.

In this program, the line "int A=0;" creates an object of type int with the first name "A".

When you use the name of an object in an expression, you're essentially using a reference to it (technically an lvalue), so when you wrote A in the next line, the type of that A is "int&".

However, that function expects a short&, so someone decided to cast int& to short& with a C-style cast. The appropriate C++ style cast would have been reinterpret_cast<short&>(A).

Such cast is illegal because it breaks aliasing rules, so the compiler is allowed to do anything at all inside the function on the line A=10, which is where undefined behavior occurs:

Your compiler chose to do nothing, as far as we can tell, (so A remained zero),

Texan40's compiler chose to modify A (and apparently runs on little-endian architecture), so 10 was printed

My compilers printed 0 (gcc) and 655360 (xlc, acc, sun CC)

other compilers may do other things - they may crash if they want to, anything is allowed under undefined behavior.

The intent of the original programmer was likely to store the value 10 in the first sizeof(short) bytes of main's A.

(note that "void main" is not valid C++ either)

EDIT: there are no unnamed temporary objects involved, guys.
Last edited on May 31, 2012 at 9:39pm
May 31, 2012 at 9:31pm
The cast is probably making a temporary. The compiler is probably doing something similar to this:
1
2
3
int A=0;
short temporary = A;
myFunction(temporary);


In which case the temporary is being modified, and not A.

Really, if the function takes a short&, you pretty much have to give it a short&. You can't give it an int&.


EDIT:

holy crap what happened, I thought there weren't any replies to this yet.
Last edited on May 31, 2012 at 9:32pm
May 31, 2012 at 9:35pm
printf("A=%d\r\n",A); /* A=0 will be the output */


And result shall be 10 not 0.

I think that you get 0 because you compile your program as C program. You should it compile as C++ program.


@Disch
The cast is probably making a temporary


No it is incorrect. An lvelue reference will be created. So the original value will be changed.

@Cubbi
Such cast is illegal because it breaks aliasing rules, so the compiler is allowed to do anything at all inside the function on the line A=10, which is where undefined behavior occurs:


It is not undefined behavior. It is a defined behavior. The object will be interpretated as short.
Last edited on May 31, 2012 at 9:48pm
May 31, 2012 at 9:49pm
vlad wrote:
It is not undefined behavior. It is a defined behavior.


Since you like quoting the standard, please read what happens after such cast.
May 31, 2012 at 9:55pm
And what shall I read? The standard does not say about an undefined behaviour in this case. So the result is expected and will be equal to 10.
May 31, 2012 at 10:00pm
vlad wrote:
The standard does not say about an undefined behaviour in this case

You are wrong. The standard says quite explicitly that the behavior is undefined (not the cast itself, it always succeeds, but the glvalue access afterwards, at A=10)
Last edited on May 31, 2012 at 10:02pm
May 31, 2012 at 10:07pm
¿Who cares? The code is horrendous and should not be used.
May 31, 2012 at 10:10pm
I have found nothing that would say that the behavior of the example is undefined.
May 31, 2012 at 10:13pm
@ne555 Basic concepts such as this are pretty important to care about. Just like order of evaluation, the aliasing rules are mysteriously absent from most C++ (and C, where C++ got the idea) textbooks and courses.

Also I'd like to make a point that the standard is not a textbook, some otherwise obvious facts may be missing a helpful hyperlink.

Hint, the following program is implementation-defined, and no longer exhibits undefined behavior:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
void myFunction(char& A)
{
    A=10;
}

int main()
{
    int A=0;
    myFunction( (char&)A );
    printf("A=%d\n", A);
}


because accessing any object through a reference to char is permitted.
Last edited on May 31, 2012 at 10:17pm
May 31, 2012 at 10:18pm
Consider another example when you need to write the length of the string in the first two bytes of a character array.

1
2
3
4
5
6
7
8
9
10
void f( short &x, short length )
{
   x = length;
}

char s[256] = {};

// some filling of s starting from the third byte

f( ( short & ) s, length );
May 31, 2012 at 10:22pm
I think §3.10/10 in C++11 is the relevant part. This rule is often called the strict aliasing rule.
Last edited on May 31, 2012 at 10:26pm
May 31, 2012 at 10:35pm
I do not understand why you reference to glvalue and not lvalue which will be the result of discussed casting.
May 31, 2012 at 10:44pm
An lvalue is a glvalue.
May 31, 2012 at 11:22pm
@Peter87 Spoilers!

@vlad: your sample also invokes undefined behavior. GCC was even nice enough to issue a warning, although the resulting binary behaved as you probably expected.

Topic archived. No new replies allowed.