Revistiing using return value as reference

J

Jim Langston

I remember there was a thread a while back that was talking about using the
return value of a function as a reference where I had thought the reference
would become invalidated because it was a temporary but it was stated that
it would not. This has come up in an irc channel but I can not find the
original thread, nor can I get any code to work.

Foo& Bar( int Val )
{
return Foo( Val );
}

Will not work, can not convert Foo to Foo&

Foo Bar( int Val )
{
return Foo( Val );
}

int main()
{
Foo& Inst = Bar( 10 );
}

Does not work, same thing, can not convert Foo to Foo&.

Foo& Bar(int Val )
{
Foo Temp( Val );
return Foo( Val );
}

int main()
{
Foo& Inst = Bar( 10 );
}

Does not work, checking the value of Val_ later shows garbage data
indicating the reference has become invalidated (as expected). Can anyone
remember the case where a temp value returned from a function can be used as
a reference and not invalidated immediately?
 
K

Kira Yamato

I remember there was a thread a while back that was talking about using the
return value of a function as a reference where I had thought the reference
would become invalidated because it was a temporary but it was stated that
it would not. This has come up in an irc channel but I can not find the
original thread, nor can I get any code to work.

Foo& Bar( int Val )
{
return Foo( Val );
}

Will not work, can not convert Foo to Foo&

Foo Bar( int Val )
{
return Foo( Val );
}

int main()
{
Foo& Inst = Bar( 10 );
}

Does not work, same thing, can not convert Foo to Foo&.

Foo& Bar(int Val )
{
Foo Temp( Val );
return Foo( Val );
}

int main()
{
Foo& Inst = Bar( 10 );
}

Does not work, checking the value of Val_ later shows garbage data
indicating the reference has become invalidated (as expected). Can anyone
remember the case where a temp value returned from a function can be used as
a reference and not invalidated immediately?

Foo Bar(int x)
{
return Foo(x);
}

int main()
{
const Foo &foo = Bar(10);
return 0;
}
 
J

johanatan

Foo& Bar( int Val )
{
return Foo( Val );

}

Will not work, can not convert Foo to Foo&

Should work, but not a very useful thing to do as local copy dies
immediately.

Foo Bar( int Val )
{
return Foo( Val );

}

int main()
{
Foo& Inst = Bar( 10 );

}

Does not work, same thing, can not convert Foo to Foo&.

Should also work. Something else is going on as that syntax is
compiling fine in my compiler.
Foo& Bar(int Val )
{
Foo Temp( Val );
return Foo( Val );

}

I think you meant to return Temp, no? That too should work, but as
with #1, isn't very useful.
int main()
{
Foo& Inst = Bar( 10 );

}

Does not work, checking the value of Val_ later shows garbage data
indicating the reference has become invalidated (as expected). Can anyone
remember the case where a temp value returned from a function can be used as
a reference and not invalidated immediately?

Here's some thoughts:

Foo g_foo;

class Y
{
protected:
Foo _x; // instance (or member) variable
static Foo _y; // class instance variable (i.e., a static)

public:
Foo& GetFoo();
Foo& GetFoo2();
};

Foo&
Y::GetFoo()
{
return _x;
}

Foo&
Y::GetFoo2()
{
static Foo localStaticFoo; // global to all instances of Y but only
accessible within GetFoo2.
return localStaticFoo;
}

int main()
{
Y y;
Foo& foo = y.GetFoo();
Foo& foo2 = y.GetFoo2();
Foo& foo3 = g_foo;
}

A reference has to 'point' to a preexisting instance of something
(i.e., must be initialized to something upon declaration-- note that
this also includes any references that you might have as class members
which must be initted in your constructors' init lists).

In your case where that instance was local, it satisfied your
compiler, but obviously didn't have any use as it was deleted
underneath you when the func exited. So, you have to couple that idea
with a longer living object (such as class member, static member, or
global).
 
E

Erik Wikström

Should work, but not a very useful thing to do as local copy dies
immediately.

Should not work, a reference can not bind to a rvalue.
Should also work. Something else is going on as that syntax is
compiling fine in my compiler.

Get a new compiler. Once again, you can not bind a reference to a rvalue.
I think you meant to return Temp, no? That too should work, but as
with #1, isn't very useful.

Invoking undefined behaviour I would think.
 
J

johanatan

Should not work, a reference can not bind to a rvalue.

Ok, you're right. I just tried this again in Ch (a C++ interpreter)
-- apparently the method wasn't being called before and so wasn't
compiled.
Get a new compiler. Once again, you can not bind a reference to a rvalue.

This is certainly an interesting design decision of C++. I suspect it
was a 'safety' feature built in, but I prefer languages to be more
flexible by inferring as much as possible from context. What is the
danger of binding to an r-value? A value is a value (and if you were
thinking in purely functional terms, this is just a transformation
that happens in one line instead of two). Does it really help things
to declare the Foo on one line return it instead of the constructor
call? Substitution (i.e., rewriting) is the name of the game. Why
prevent this level of compaction?

--Jonathan
 
P

Pavel Shved

This is certainly an interesting design decision of C++. šI suspect it
was a 'safety' feature built in, but I prefer languages to be more
flexible by inferring as much as possible from context. šWhat is the
danger of binding to an r-value? šA value is a value (and if you were
thinking in purely functional terms, this is just a transformation
that happens in one line instead of two). šDoes it really help things
to declare the Foo on one line return it instead of the constructor
call? šSubstitution (i.e., rewriting) is the name of the game. šWhy
prevent this level of compaction?

We prefer choosing language after the problem to be solved, not
before. If you `prefer' languages, then go ahead, write all your
programs on perl which surely allows you to return valid reference to
local objects.

As far as i understand, that's not a `safety' feature, like `const' is
not it as well. It's just a way to imbue pre- and postconditions on
our *source* code. I mean, C++ compiler is so strict because it
encourages programmers to write a program verifiable to some extent.
A program driven by underlying ideas, by underlying concepts and
restrictions (not all! just a couple of, but this little effort makes
its benifit) of their models. But not vice versa which as you seem to
misunderstand is not the right way of writing programs.

That leads to a nice and usefult thing: if your program doesn't fit
your model's requirements it won't compile. Which, in turn, saves
time spent on debugging.
 
E

Erik Wikström

Ok, you're right. I just tried this again in Ch (a C++ interpreter)
-- apparently the method wasn't being called before and so wasn't
compiled.


This is certainly an interesting design decision of C++. I suspect it
was a 'safety' feature built in, but I prefer languages to be more
flexible by inferring as much as possible from context. What is the
danger of binding to an r-value?

Consider the following code:

int& r = 5;

r = 6;

If I can bind a reference to a value I can also change that value. But
that is not how a reference works, a reference is a alias for an object
not an object on its own. Therefore a reference can not bind to a rvalue.
A value is a value (and if you were thinking in purely functional
terms, this is just a transformation that happens in one line instead
of two).

But C++ is not a functional language, thinking in functional terms will
lead you astray.
Does it really help things to declare the Foo on one line return it
instead of the constructor call? Substitution (i.e., rewriting) is
the name of the game. Why prevent this level of compaction?

Because when you declare the object first and then returns it you have
an lvalue, which you can bind a reference to.
 
A

Andrew Koenig

What is the danger of binding to an r-value?

If it is possible to bind a reference to an rvalue, then there is no basis
for the compiler to reject:

int x = 0, y = 0;
cin >> (x + y);
 
J

johanatan

Consider the following code:

int& r = 5;

r = 6;

If I can bind a reference to a value I can also change that value. But
that is not how a reference works, a reference is a alias for an object
not an object on its own. Therefore a reference can not bind to a rvalue.

Ok, so I suspect it was decided early on that the easiest way to
prevent 'changing' a reference was to disallow setting it to *any* r-
value? That surely simplifies writing a compiler for the language,
but seems like a pretty arbitrary restriction to place on programmers
(and it seems possible to me to make a compiler that could
differentiate between an initial setting and subsequent 'changes').

And, another question that follows naturally-- 'Why aren't references
allowed to be changed to begin with?' If they were, then the r-value
problem goes away and then the integration of boost's 'shared_ptr'
functionality would be much more seamless at this time.

I really would have preferred some sort of new syntax to the language
itself instead of tacking on another lib as part of the 'standard'.
For instance, you could use:

int^ x;

to represent a 'smart' ptr in the same way that 'int&' is a reference.
But C++ is not a functional language, thinking in functional terms will
lead you astray.

Well, when you write:

int x = g(f(y));

you're doing 'functional' stuff (aside from the storage in a
variable). My point was that the practice of programming boils down
to 'rewrites', or substitutions or transformations. Even when in an
imperative language, much of the job is about writing one thing to
represent another (i.e., symbols).
Because when you declare the object first and then returns it you have
an lvalue, which you can bind a reference to.

Yea, but, once again, that's only because of the (as far as I can
tell) arbitrary decision to disallow setting to r-value. I think
there's a misunderstanding here between what is 'required' by the
syntax of the language and the requirements imposed upon the language
by external forces (at least in my mind).

--Jonathan
 
J

johanatan

If it is possible to bind a reference to an rvalue, then there is no basis
for the compiler to reject:

int x = 0, y = 0;
cin >> (x + y);

Hmm... that's interesting. One possible way to deal with that is to
let it put the input into an unnamed variable (which would be
inaccessible unless you expose some sort of global environment a la
python).

If you think that type of programming error is so likely that this
restriction is a net gain, then I guess it's a win. I'm just not
completely convinced yet. :)

--Jonathan
 
E

Erik Wikström

Ok, so I suspect it was decided early on that the easiest way to
prevent 'changing' a reference was to disallow setting it to *any* r-
value?

You can never change a reference, only the object it refers to.
That surely simplifies writing a compiler for the language,
but seems like a pretty arbitrary restriction to place on programmers
(and it seems possible to me to make a compiler that could
differentiate between an initial setting and subsequent 'changes').

I think it makes sense, you can not create a reference to an object that
can no be changed. Call it a safety mechanism that prevents users from
making stupid mistakes.

Of course that does not prevent one from referring to r-values, but you
need to have a const reference (to ensure that the user does not modify
the rvalue through the reference).
And, another question that follows naturally-- 'Why aren't references
allowed to be changed to begin with?' If they were, then the r-value
problem goes away and then the integration of boost's 'shared_ptr'
functionality would be much more seamless at this time.

If you could change a reference then you would have a pointer, and we
already have those. And as we all know pointers are not very safe to
play with. Not sure what you mean with shared_ptr would be more seam-
lessly integrated, though.
I really would have preferred some sort of new syntax to the language
itself instead of tacking on another lib as part of the 'standard'.
For instance, you could use:

int^ x;

to represent a 'smart' ptr in the same way that 'int&' is a reference.

Personally I prefer to keep the stuff in the language to a minimum and
have a library which provides the stuff I need, but that is just me. If
you want a language where everything (well, at least a lot) is hidden
from the programmer with syntactic sugar try C#.
Well, when you write:

int x = g(f(y));

you're doing 'functional' stuff (aside from the storage in a
variable).

In a functional language the above would likely mean that g() was a
function taking a function which takes one argument as argument, while
in C++ it means that g() is called with the value returned from f(x).
My point was that the practice of programming boils down
to 'rewrites', or substitutions or transformations. Even when in an
imperative language, much of the job is about writing one thing to
represent another (i.e., symbols).

Yes, programming is all about collecting information and then
transforming it into something else.
Yea, but, once again, that's only because of the (as far as I can
tell) arbitrary decision to disallow setting to r-value. I think
there's a misunderstanding here between what is 'required' by the
syntax of the language and the requirements imposed upon the language
by external forces (at least in my mind).

I think that Andrew Koenig's example makes it quite clear why you can
not bind a reference to an r-value, in many cases it simply does not
make sense. I have two questions for you:

1. Are you aware of and understand the difference between a reference
and a const reference?

2. Can you give an example of when it would be useful to bind a
reference to a r-value?
 
P

Pavel Shved

2. Can you give an example of when it would be useful to bind a
reference to a r-value?

void retransfer(Object& x)
{
x.timestamp=now();
x.transferred=true;
buf b = allocate_superbuffer(sizeof(x));
copy_to_superbuffer(b,x);
}

retransfer (a); //we want to mark Object a as transferred and pass
it along
retransfer (b+c); //we want to pass result of b+c along and do not
care much what happens to temporary variable of Object type the result
of operator+ was assigned to.

Yes, i know. I can easilly write

Object d=b+c;
retransfer(d);

but why? What if i really *don't care* and moreover i *don't want* to
take care of where result of this sum will go? Why should i write one
more line of code (!) if i really don't need it? A man reading my
code would surely be surprised, `Why does he need this d variable??'

Well, one can make more life-bound example, but this one shows that
sometimes we should really sacrifice a hamster for the compiler to get
an elephant past the corner.
 
J

jkherciueh

Erik said:
2. Can you give an example of when it would be useful to bind a
reference to a r-value?

Consider:

std::vector<int> v;
...
v.swap( std::vector<int>() ); // fails
swap( v, std::vector<int>() ); // fails
std::vector<int>().swap( v ); // succeeds

It is especially bad that the middle line fails. The rules of [8.3.5/5] make
it difficult to define swap()-like functions for proxy classes. E.g., it is
somewhat tricky to have a line like

row_swap( A.row(1), A.row(2) );

do a row-swap in a matrix class and be const-correct.


Another surprise caused by the rules of [8.3.5/5] (and frequently coming up
in this group in various disguises) is this:

int main ( void ) {
std::eek:fstream( "test.001" ) << std::dec << "hello world!" << '\n';
std::eek:fstream( "test.002" ) << "hello world!" << '\n';
}

On my machine, this creates files like so:

test.001: hello world!
test.002: 0x8048a01



Best

Kai-Uwe Bux
 
K

Kira Yamato

Erik said:
2. Can you give an example of when it would be useful to bind a
reference to a r-value?

Consider:

std::vector<int> v;
...
v.swap( std::vector<int>() ); // fails
swap( v, std::vector<int>() ); // fails
std::vector<int>().swap( v ); // succeeds

It is especially bad that the middle line fails. The rules of [8.3.5/5] make
it difficult to define swap()-like functions for proxy classes. E.g., it is
somewhat tricky to have a line like

row_swap( A.row(1), A.row(2) );

do a row-swap in a matrix class and be const-correct.


Another surprise caused by the rules of [8.3.5/5] (and frequently coming up
in this group in various disguises) is this:

int main ( void ) {
std::eek:fstream( "test.001" ) << std::dec << "hello world!" << '\n';
std::eek:fstream( "test.002" ) << "hello world!" << '\n';
}

On my machine, this creates files like so:

test.001: hello world!
test.002: 0x8048a01

o_O This is truly unexpected!

I'm guessing this is an instance of calling the member version of
operator<< verses the nonmember version.

In the first line with "test.001", the 1st call to the member version
of operator<<(std::dec) returns a reference to an object, which then
can be used in the nonmember version of operator<<(ostream &, const
char *).

However, in the second line with "test.002", the expression
std::eek:fstream("test.002") is a temporary object which cannot be bound
to the 1st parameter of the nonmember version operator<<(ostream &,
const char *). Hence, it resorts to some member version of operator<<
which converted const char * to some integer.

Wow. When I thought I've seen the worst, I'm being surprise more and
more to see that C++ truly has some totally-unobvious gotcha's.

I hope the next revision of C++ will address some of these strange codes.
 
J

johanatan

You can never change a reference, only the object it refers to.

I understand. But, the question was:

is the prevention of setting a reference to *any* r-value an easy way
for a compiler to enforce the 'set only once' rule for references, and
if so, was that the rationale for the set-once rule?
If you could change a reference then you would have a pointer, and we
already have those. And as we all know pointers are not very safe to
play with. Not sure what you mean with shared_ptr would be more seam-
lessly integrated, though.

But, the point of a reference is to hide that you have a pointer. The
restriction that it be set only once just seems somewhat arbitrary.
After all, in Java and C#, languages where references (and for that
matter, reference-counted smart ptrs) can be set multiple times (and
even to NULL).
Personally I prefer to keep the stuff in the language to a minimum and
have a library which provides the stuff I need, but that is just me. If
you want a language where everything (well, at least a lot) is hidden
from the programmer with syntactic sugar try C#.

Well, my problem with all the libs for doing this stuff in C++ is the
klunkiness of the syntax. Just take a look inside <functional> (STL
header) sometime to see lots of cool features that are added with
klunky syntax.
In a functional language the above would likely mean that g() was a
function taking a function which takes one argument as argument, while
in C++ it means that g() is called with the value returned from f(x).

Not necessarily. In a purely functional language, the x wouldn't be
specified. And, once it is specified, the 'f' that operates on that
specific x comes into existence and the 'g' can subsequently operate
on it. Having a 'g' that can operate on many types of 'f' was not the
point of my example (maybe it was a bad example). But, the point was
that in C++, as well as functional langs, functions transform input
(whether the input is a function or not) and their output can be piped
right into another function, hence resulting, in essence, to a
'transliteration'.

It will certainly be a nice addition to C++ when first-class functions
arrive.
1. Are you aware of and understand the difference between a reference
and a const reference?

Yes. With a const reference, you can only call 'const' member
functions (assuming its an object). With a non-const reference, you
can modify at will. Just like anything else const or non-const I
suppose.
2. Can you give an example of when it would be useful to bind a
reference to a r-value?

I can't. But, just scrolling down, the other examples posted are very
nice! In general, every restriction that a lang places is going to
have unintended side effects and there's always a trade-off between
flexibility/power and 'safeness'. In some situations, you just end up
writing code to satisfy a compiler when you know better. ;-)

I don't think that is the case with 'const-correctness' (as another
poster mentioned) of C++, but even that requirement might have corner
cases that I am missing right now. Const-correctness and setting an r-
value into a reference are in two different leagues in my opinion.

--Jonathan
 
J

johanatan

We prefer choosing language after the problem to be solved, not
before. If you `prefer' languages, then go ahead, write all your
programs on perl which surely allows you to return valid reference to
local objects.

I wasn't speaking with any 'problem' in mind except that of specifying
for a computing machine that work which it shall do. I actually
learned to program with C++ and have since moved to Perl and other
'dynamic' languages (Python and Ruby), and others, and am now back
with C++. I've gotta say that moving to those dynamic langs was
definitely a 'freeing' experience, but even better than those, from a
purely theoretical point of view, in my opinion, is my current
interest--Haskell. It has all the benefits of a strongly typed
language and the terseness of functional langs. For me, the closer to
the math a lang is, the more concise and clean it is, the more elegant
and beautiful. After all, programming for the sake of programming
should not be discounted.

The only rationale for using C++ that I've heard lately is for
performance as there are many other modern O-O langs to choose from
(some are even compiled and can perform at least as well such as
Objective-C, but that really only applies if your platform is Apple).
As far as i understand, that's not a `safety' feature, like `const' is
not it as well. It's just a way to imbue pre- and postconditions on
our *source* code. I mean, C++ compiler is so strict because it
encourages programmers to write a program verifiable to some extent.
A program driven by underlying ideas, by underlying concepts and
restrictions (not all! just a couple of, but this little effort makes
its benifit) of their models. But not vice versa which as you seem to
misunderstand is not the right way of writing programs.

That leads to a nice and usefult thing: if your program doesn't fit
your model's requirements it won't compile. Which, in turn, saves
time spent on debugging.

Yea, I definitely agree with this point (especially as it applies to
const-correctness and type-safety). But, even those have unintended
side effects which are a pain to work through at times. Not
everything that is done to satisfy compilers (in fact, a substantial
amount, I think) actually saves time or prevents bugs.

--Jonathan
 
E

Erik Wikström

I understand. But, the question was:

is the prevention of setting a reference to *any* r-value an easy way
for a compiler to enforce the 'set only once' rule for references, and
if so, was that the rationale for the set-once rule?

No, even if you could bind a reference to a r-value you would not be
able to rebind it later on. That is the way references were designed to
work.
But, the point of a reference is to hide that you have a pointer. The
restriction that it be set only once just seems somewhat arbitrary.
After all, in Java and C#, languages where references (and for that
matter, reference-counted smart ptrs) can be set multiple times (and
even to NULL).

That is because the references of Java and C# are more like pointers
than C++ references, theonly similarity between Java/C# references and
C++ references is the name. The references in C++ are a totally
different concept, in C++ a reference is an alias for an object, if you
think of it that way you can see why they may not be reseated.
Yes. With a const reference, you can only call 'const' member
functions (assuming its an object). With a non-const reference, you
can modify at will. Just like anything else const or non-const I
suppose.

And a const reference can bind to an r-value.
I can't. But, just scrolling down, the other examples posted are very
nice!

Ah, I am starting to see your point (I might be a bit slow, but I
usually get there). You might want to check out the Rvalue references,
which will be part of the next version of C++. I think it could be used
to solve all the problems you, Kai-Uwe Bux, and Pavel Shved mentioned. A
quick overview:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html
 
J

johanatan

That is because the references of Java and C# are more like pointers
than C++ references, theonly similarity between Java/C# references and
C++ references is the name. The references in C++ are a totally
different concept, in C++ a reference is an alias for an object, if you
think of it that way you can see why they may not be reseated.

That just isn't true (on both technical and conceptual levels).
Technically, a reference in C++ is just a ptr with syntactic sugar to
allow using it just like the object itself instead of dereferencing
the ptr (or using the -> operator). Conceptually, they are the same
in the two languages (barring the restrictions already mentioned that C
++ enforces). The only differences I'm aware of between C#/Java and C+
+ with regards to references are:

-. Java references are 'smart' references (i.e., ref counted).
-. Java references can be assigned to NULL.
-. Java references can be set multiple times (and have no restrictions
against setting to r-values).

Under the skin, they are both ptrs (while the Java variant has an
additional ref count and possibly other bookeeping-related (i.e.,
garbage collector) metadata).

I suspect that the reference idea was a step forward for C++ from C
(adding only the syntactic sugar over a regular ptr) and that the
designers of Java took the idea one step further.

--Jonathan
 
J

johanatan

void retransfer(Object& x)
{
  x.timestamp=now();
  x.transferred=true;
  buf b = allocate_superbuffer(sizeof(x));
  copy_to_superbuffer(b,x);

}

retransfer (a);    //we want to mark Object a as transferred and pass
it along
retransfer (b+c);  //we want to pass result of b+c along and do not
care much what happens to temporary variable of Object type the result
of operator+ was assigned to.

Well, if C++ will allow binding an r-value to a const reference
(something I haven't verified yet, but makes sense), then you could
factor out the part of retransfer that modifies the object from the
part that simply copies it. Such as:

void copyToSuperBuf(const Object& o)
{
buf b = allocate_superbuffer(sizeof(o));
copy_to_superbuffer(b,o);
}

void retransfer(Object& o)
{
o.timestamp=now();
o.transferred=true;
copyToSuperBuf(o);
}

but that doesn't preclude there being another example that couldn't be
broken up this way I suppose.

--Jonathan
 
D

Dave Steffen

johanatan said:
That just isn't true (on both technical and conceptual levels).

No, *that* just isn't true (on both technical and conceptual
levels). Eric is exactly right: references are aliases for existing
objects. Full stop.
Technically, a reference in C++ is just a ptr with syntactic sugar
to allow using it just like the object itself instead of
dereferencing the ptr (or using the -> operator).

No indeed. Pointers can be reseated, can be null, and take a
(platform-specific) number of bytes. References cannot be reseated,
cannot be "null" (although they can be invalid), and don't have a
size.

The compiler may *implement* references internally as pointers, but
that's irrelevent to this discussion.
Conceptually, they are the same in the two languages (barring the
restrictions already mentioned that C ++ enforces).

I think they were *supposed* to be conceptually the same in Java and
in C++, but it didn't really work out that way.
-. Java references are 'smart' references (i.e., ref counted).
-. Java references can be assigned to NULL.
-. Java references can be set multiple times (and have no restrictions
against setting to r-values).

Which are all things you can do with C++ *pointers*, and cannot be
done with C++ *references*. Which is what Eric said, and which I
reiterated.
Under the skin, they are both ptrs (while the Java variant has an
additional ref count and possibly other bookeeping-related (i.e.,
garbage collector) metadata).

Under the skin, C++ references are C++ references, period.
I suspect that the reference idea was a step forward for C++ from C
(adding only the syntactic sugar over a regular ptr) and that the
designers of Java took the idea one step further.

I'd suggest that Java took the idea one step backwards, but that's a
different debate. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,985
Messages
2,570,199
Members
46,766
Latest member
rignpype

Latest Threads

Top