Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Dereferencing and returning by reference!

208 views
Skip to first unread message

Prasoon Saurav

unread,
May 14, 2013, 5:26:57 PM5/14/13
to

Consider a small unit test case

struct A
{
virtual void func(){}
A& foo()
{
A *obj = reinterpret_cast<A*>(0xdeadbeef);
return *obj; //1
}
};

int main()
{
A obj = obj.foo();
}

At line 1 is it implementation defined/unspecified that the deference
would not happen as we are returning by reference and the program
would not crash if an explicit access to the pointed to object is not
made?

I had argument with one of my colleagues wherein he mentioned that the
compiler, in most cases, would optimize the dereference of obj as we
are returning it by reference and this code would not crash?

Does the standard say anything on this?

I had asked the same query on stackoverflow.com but didn't get any
good answer as such.

Thanks


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Bart van Ingen Schenau

unread,
May 15, 2013, 6:47:47 AM5/15/13
to

On Tue, 14 May 2013 15:26:57 -0600, Prasoon Saurav wrote:

> Consider a small unit test case
>
> struct A
> {
> virtual void func(){}
> A& foo()
> {
> A *obj = reinterpret_cast<A*>(0xdeadbeef); return *obj; //1
> }
> };
>
> int main()
> {
> A obj = obj.foo();
> }
>
> At line 1 is it implementation defined/unspecified that the
> deference would not happen as we are returning by reference and the
> program would not crash if an explicit access to the pointed to
> object is not made?
>
> I had argument with one of my colleagues wherein he mentioned that
> the compiler, in most cases, would optimize the dereference of obj
> as we are returning it by reference and this code would not crash?
>
> Does the standard say anything on this?

The C++ standard describes the behavior of C++ programs when they are
executed on an abstract machine that follows the semantics of the C++
standard exactly. Real implementations are given permission to deviate
from the idealized abstract machine in any way they like, as long as
the outwardly observable behavior of *correct* programs is not
affected.

In the case at hand, the dereference at line 1 happens
unconditionally, but a conforming compiler can optimize it away on the
grounds that a correct program will not be able to tell the
difference. That you could tell the difference (crash or not) is
because the program is incorrect. It produces Undefined Behavior on
the grounds of dereferencing a pointer that does not refer to a valid
object.

When a program contains Undefined Behavior, the C++ standard absolves
itself from responsibility and declares whatever result you get as
being valid. This includes the physically improbable, like setting
fire to your pants, or the inconsistent, like giving different results
in an important presentation without recompiling.

In the end, it might crash or it might not and there is no way to tell
which it will do on the next execution.

Bart v Ingen Schenau

Richard Corden

unread,
May 15, 2013, 6:45:09 AM5/15/13
to

Hi,

On 05/14/2013 11:26 PM, Prasoon Saurav wrote:
>

[...]

> A& foo()
> {
> A *obj = reinterpret_cast<A*>(0xdeadbeef);
> return *obj; //1
> }
[...]


> At line 1 is it implementation defined/unspecified that the
> deference would not happen as we are returning by reference and the
> program would not crash if an explicit access to the pointed to
> object is not made?

The last line of:
http://std.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#232

Has:
We agreed that the approach in the standard seems okay: p = 0; *p;
is not inherently an error. An lvalue-to-rvalue conversion would
give it undefined behavior.


I believe the intent is that, as return of '*obj' doesn't involve an
'lvalue to rvalue' conversion there is no problem at this point.

[...]

> int main()
> {
> A obj = obj.foo();
> }

Here, however, there is an 'lvalue to rvalue' conversion and so this
is a problem.


Regards,

Richard



--
Richard Corden

Daniel Krügler

unread,
May 15, 2013, 10:54:54 AM5/15/13
to

On 2013-05-15 12:45, Richard Corden wrote:
> On 05/14/2013 11:26 PM, Prasoon Saurav wrote:
>>
>
> [...]
>
>> A& foo()
>> {
>> A *obj = reinterpret_cast<A*>(0xdeadbeef);
>> return *obj; //1
>> }
> [...]
>
>> At line 1 is it implementation defined/unspecified that the
>> deference would not happen as we are returning by reference and the
>> program would not crash if an explicit access to the pointed to
>> object is not made?
>
> The last line of:
> http://std.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#232
>
> Has:
> We agreed that the approach in the standard seems okay: p = 0;
> *p; is not inherently an error. An lvalue-to-rvalue conversion
> would give it undefined behavior.
>
> I believe the intent is that, as return of '*obj' doesn't involve an
> 'lvalue to rvalue' conversion there is no problem at this point.

I agree that the mentioned issue is related to this problem. The most
recent issue state can be found here:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232

Note that the current resolution tendency (the issue is still
drafting) seems to indicate that only references to a null object or
to a one past the last element of an array would be feasible, not a
reference to something arbitrary would be supported. In this case, the
example part - even the one that would restrict to the following
variation

struct A
{
virtual void func(){}
A& foo()
{
A *obj = reinterpret_cast<A*>(0xdeadbeef);
return *obj; //1
}
};

int main()
{
A& obj = obj.foo();
}

would still have undefined behaviour, because
"reinterpret_cast<A*>(0xdeadbeef)" would not satisfy any of the
criteria.

HTH & Greetings from Bremen,

- Daniel Kr�gler




--

Wil Evers

unread,
May 15, 2013, 4:52:14 PM5/15/13
to
Daniel Kr�gler wrote:

> The most recent issue state can be found here:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
>
> Note that the current resolution tendency (the issue is still
> drafting) seems to indicate that only references to a null object or
> to a one past the last element of an array would be feasible

I'm surprised. Is the committee seriously thinking about legalizing
the act of forming a reference from either a null pointer or a pointer
one past the last element of an array?

This goes against my religion. It seems to imply that it's no longer
up to the programmer forming the reference to ensure it refers to a
real object. Instead, it would be up to the programmer who needs to
access the referenced object to somehow determine it actually exists.
We'd have to write stuff like:

void swap_ints(int& x, int& y)
{
if (&x != nullptr && &y != nullptr) {
int tmp = x; x = y; y = tmp;
} else {
// now what? abort?
}
}

Not even the above is good enough though, because it doesn't cater for
the one-past-the-end case. In general, I simply wouldn't know how to
check for that.

For years, I've been telling others to use references when they can,
and only use pointers when they must. It seems I was wrong.

- Wil

alan_mc...@this.is.invalid

unread,
May 15, 2013, 5:21:27 PM5/15/13
to
On Wednesday, May 15, 2013 10:00:02 AM UTC-4, Daniel Kr�gler wrote:

> > The last line of:
> > http://std.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#232
> >
> > Has: We agreed that the approach in the standard seems okay:
> > p = 0;; is not inherently an error. An lvalue-to-rvalue conversion
> > would give it undefined behavior.
> >
> > I believe the intent is that, as return of '*obj' doesn't involve
> > an 'lvalue to rvalue' conversion there is no problem at this
> > point.
>
> I agree that the mentioned issue is related to this problem. The
> most recent issue state can be found here:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
>
> Note that the current resolution tendency (the issue is still
> drafting) seems to indicate that only references to a null object or
> to a one past the last element of an array would be feasible, not a
> reference to something arbitrary would be supported.

What is the point of endorsing binding a reference to a "null object"
or one past the end of an array, even if you forbid binding them to
the target of a garbage pointer value?

I say "endorsing" rather than "allowing" because, most (but not all!)
implementations effectively "allow" you to do most anything with an
invalid pointer value except actually access the pointee and sometimes
even accessing it will "work," for some suitable interpretation of
"work," and a reference is (usually) an implicitly dereferenced
pointer.

Up until I read this post, I assumed that the intent of (C++)
reference variables was that they would always be expected to be bound
to something you could actually reference -- that it was undefined
behavior and hence a no-no to do otherwise. This comment suggests
that the C++ is endorsing having un-referenceable references.

This is IMHO a Bad Thing(tm); Wil Evers' post gives one example of
why.

Or am I missing something?

Daniel Krügler

unread,
May 16, 2013, 12:14:59 AM5/16/13
to
Thanks for your feedback, I have forwarded your concerns to the C++
committee for consideration.

One important point to mention is that this issue is very old and
still unresolved, so at the very moment there is no reason to panic
;-) In addition to that, with the presence of constexpr in the
language, compilers have now much more obligation to detect code that
would cause undefined behavior in several contexts. This presumably
gives a new light onto allowing such "empty lvalues" and the whole
issue needs to get a fresh start as it seems. There also occurred a
new core language issue

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1652

that is related to the compile-time/runtime frontier crossing.

HTH & Greetings from Bremen,

Daniel Kr�gler

Daniel Krügler

unread,
May 16, 2013, 8:52:42 AM5/16/13
to
On 2013-05-15 23:21, alan_mc...@this.is.invalid wrote:
> On Wednesday, May 15, 2013 10:00:02 AM UTC-4, Daniel Kr�gler wrote:
>
>>> The last line of:
>>> http://std.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#232
>>>
>>> Has: We agreed that the approach in the standard seems okay: p =
>>> 0;; is not inherently an error. An lvalue-to-rvalue conversion
>>> would give it undefined behavior.
>>>
>>> I believe the intent is that, as return of '*obj' doesn't involve
>>> an 'lvalue to rvalue' conversion there is no problem at this
>>> point.
>>
>> I agree that the mentioned issue is related to this problem. The
>> most recent issue state can be found here:
>>
>> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
>>
>> Note that the current resolution tendency (the issue is still
>> drafting) seems to indicate that only references to a null object
>> or to a one past the last element of an array would be feasible,
>> not a reference to something arbitrary would be supported.
>
> What is the point of endorsing binding a reference to a "null
> object" or one past the end of an array, even if you forbid binding
> them to the target of a garbage pointer value?

First, I need to correct my initial judgement a bit (thanks to Richard
Smith for pointing that out to me): CWG 232 still doesn't make binding
*references* to "null objects" valid, it only allows to form empty
glvalues in some contexts. The difference is that

A* p1 = nullptr;
A* p2 = &*p1;

would become valid, but still attempting to form

A& r2 = *p1;

would be undefined behaviour. Supporting this idiom - especially for
arrays in the form of

int a[10];
int* pa = &a[10];

- would just be consistent with what C99 allows (but not C89).

It makes sense to add in this context that there exists indeed an
issue that follows more the direction I was describing when
incorrectly referring to CWG 232, which is:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#453

Again, this is still drafting, it needs further adaption in the
context of constexpr, and any use of such a reference would still be
undefined behavior as it occurred in the revised example I mentioned
in my previous posting.

> Up until I read this post, I assumed that the intent of (C++)
> reference variables was that they would always be expected to be
> bound to something you could actually reference -- that it was
> undefined behavior and hence a no-no to do otherwise. This comment
> suggests that the C++ is endorsing having un-referenceable
> references.
>
> This is IMHO a Bad Thing(tm); Wil Evers' post gives one example of
> why.
>
> Or am I missing something?

Neither you nor Wil are missing something, there is currently no
intended support for null references by the core language group. I
apologize for my misleading original reply.

HTH & Greetings from Bremen,

Daniel Kr�gler
0 new messages