473,569 Members | 2,617 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Why isn't the lifetime of the temporary extended in this case?

Let's assume we have a class like this:

//---------------------------------------------------------
#include <iostream>

class MyClass
{
public:
MyClass() { std::cout << "constructor\n" ; }
~MyClass() { std::cout << "destructor \n"; }

const MyClass& print(int i) const
{
std::cout << i << std::endl;
return *this;
}
};
//---------------------------------------------------------

Now, if I create a reference to a temporary instance of this class,
the lifetime of that instance will be extended for the lifetime of the
reference. For example:

//---------------------------------------------------------
int main()
{
std::cout << "Before\n";
const MyClass& obj = MyClass(); //*
std::cout << "After\n";
obj.print(2);
}
//---------------------------------------------------------

This program will print:

Before
constructor
After
2
destructor

This is even so if the temporary is the return value of a function.
For example, let's assume we have the function:

MyClass getMyClass() { return MyClass(); }

Now if we change the line marked with //* to this:

const MyClass& obj = getMyClass(); //*

the result will still be the same. So clearly the lifetime of the return
value of a function is extended by the reference.

Now comes the puzzling part, and my actual question. Suppose that we
change the line marked with //* to this:

const MyClass& obj = MyClass().print (1); //*

Suddenly the output changes:

Before
constructor
1
destructor
After
2

Now the temporary object is destroyed after the reference assignment
ends! The second print() call is now calling a destroyed object! (Oddly
gcc doesn't issue even a warning about this.)

The same is true for:

const MyClass& obj = getMyClass().pr int(1); //*

But why? Why does the print() function returning a reference to itself
change the semantics of the lifetime of the temporary object? Why isn't
the reference extending the lifetime of the object anymore? Why does the
reference extend the lifetime of the return value of getMyClass(), but
not the lifetime of the return value of MyClass::print( )? How does it
make even sense that a reference can be created to an object which is
destroyed immediately after the reference is created?
Aug 21 '08 #1
5 1470
Juha Nieminen wrote:
Let's assume we have a class like this:

//---------------------------------------------------------
#include <iostream>

class MyClass
{
public:
MyClass() { std::cout << "constructor\n" ; }
~MyClass() { std::cout << "destructor \n"; }

const MyClass& print(int i) const
{
std::cout << i << std::endl;
return *this;
}
};
//---------------------------------------------------------

Now, if I create a reference to a temporary instance of this class,
the lifetime of that instance will be extended for the lifetime of the
reference. For example:

//---------------------------------------------------------
int main()
{
std::cout << "Before\n";
const MyClass& obj = MyClass(); //*
std::cout << "After\n";
obj.print(2);
}
//---------------------------------------------------------

This program will print:

Before
constructor
After
2
destructor

This is even so if the temporary is the return value of a function.
For example, let's assume we have the function:

MyClass getMyClass() { return MyClass(); }

Now if we change the line marked with //* to this:

const MyClass& obj = getMyClass(); //*

the result will still be the same. So clearly the lifetime of the return
value of a function is extended by the reference.

Now comes the puzzling part, and my actual question. Suppose that we
change the line marked with //* to this:

const MyClass& obj = MyClass().print (1); //*

Suddenly the output changes:

Before
constructor
1
destructor
After
2

Now the temporary object is destroyed after the reference assignment
ends! The second print() call is now calling a destroyed object! (Oddly
gcc doesn't issue even a warning about this.)

The same is true for:

const MyClass& obj = getMyClass().pr int(1); //*

But why? Why does the print() function returning a reference to itself
change the semantics of the lifetime of the temporary object?
The temporary object lives (in this case) as long as the reference that
is bound *directly* to it. The bound reference lives only long enough
to initialise the other reference (the 'obj') in this case. Right after
that the lifetime of the [temporary] reference ends, so does the
lifetime of the temporary object bound to the [original] reference, the
one your 'print' function returns.
Why isn't
the reference extending the lifetime of the object anymore?
Because the language does not require it.
Why does the
reference extend the lifetime of the return value of getMyClass(), but
not the lifetime of the return value of MyClass::print( )?
It does. But you're comparing the second-hand reference's lifetime and
there is no requirement that any reference initialised later from any
other expression is supposed to cause the temporary to live longer.

Imagine:

// using your 'MyClass' class
MyClass const& pass(MyClass const& arg) { return arg; }

MyClass const& bad = pass(pass(pass( pass(MyClass()) )));

Some might think that it is the same reference initialised by binding it
to the temporary being passed in and out of the 'pass' function and
eventually put into the 'bad' reference. But it *isn't*! The argument
of the 'pass' function and its return value are *different references*.
The return value is initialised from the argument initialised from the
temporary. So, if the rule was only about binding a ref to a temporary,
the temporary would only lives as long as the argument of the very first
'pass' function (the inner-most). The "until the full expression is
evaluated" requirement would override that in this case, so you should
see the temporary report its destruction right after the last 'pass'
returns, just before 'bad' is initialised.
How does it
make even sense that a reference can be created to an object which is
destroyed immediately after the reference is created?
The same way that a pointer can be created to an object that has already
been destroyed:

Object* foo(Object *p) {
delete p;
return p;
}

Any use of the return value of this 'foo' function will have undefined
behaviour.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Aug 21 '08 #2
Juha Nieminen <no****@thanks. invalidkirjutas :
Let's assume we have a class like this:

//---------------------------------------------------------
#include <iostream>

class MyClass
{
public:
MyClass() { std::cout << "constructor\n" ; }
~MyClass() { std::cout << "destructor \n"; }

const MyClass& print(int i) const
{
std::cout << i << std::endl;
return *this;
}
};
[...]
But why? Why does the print() function returning a reference to
itself
change the semantics of the lifetime of the temporary object? Why isn't
the reference extending the lifetime of the object anymore? Why does
the
reference extend the lifetime of the return value of getMyClass(), but
not the lifetime of the return value of MyClass::print( )? How does it
make even sense that a reference can be created to an object which is
destroyed immediately after the reference is created?
getMyClass() returns an object; MyClass::print( ) returns a reference.
Presumably, if a function is returning a reference to some object, then
it means the object lives somewhere else, i.e. the lifetime of the object
is maintained elsewhere and the caller should not worry about that.

OTOH, if a function returns an object, then its lifetime cannot be
maintained by the function itself and will be the responsibility of the
caller. C++ provides a facility for maintaining the object lifetime in
this case, namely binding to a const reference. This is a special case
introduced exactly because there would be no solution otherwise (without
copying the object, which might be impossible or unacceptable).

Warning generation is a quality of implementation issue. The creation and
destruction of the temporary object are here interspersed with a call to
the print() method, which might complicate the things for compiler (if
print() is not inlined, it would be very hard to detect the error). But
yes, having a diagnostic for such things would be nice.

regards
Paavo

Aug 21 '08 #3
Victor Bazarov wrote:
>How does it
make even sense that a reference can be created to an object which is
destroyed immediately after the reference is created?

The same way that a pointer can be created to an object that has already
been destroyed:
But I thought the whole idea of references is that they would be safer
than pointers.
Aug 22 '08 #4
>class MyClass
>{
public:
MyClass() { std::cout << "constructor\n" ; }
~MyClass() { std::cout << "destructor \n"; }

const MyClass& print(int i) const
{
std::cout << i << std::endl;
return *this;
}
};
//---------------------------------------------------------
>//---------------------------------------------------------
int main()
{
std::cout << "Before\n";
const MyClass& obj = MyClass(); //*
std::cout << "After\n";
obj.print(2);
}
//---------------------------------------------------------
>>
MyClass getMyClass() { return MyClass(); }

Now if we change the line marked with //* to this:

const MyClass& obj = getMyClass(); //*

the result will still be the same. So clearly the lifetime of the return
value of a function is extended by the reference.

Now comes the puzzling part, and my actual question. Suppose that we
change the line marked with //* to this:

const MyClass& obj = MyClass().print (1); //*

Suddenly the output changes:

Before
constructor
1
destructor
After
2
[...]
Imagine:

// using your 'MyClass' class
MyClass const& pass(MyClass const& arg) { return arg; }

MyClass const& bad = pass(pass(pass( pass(MyClass()) )));

Some might think that it is the same reference initialised by binding it
to the temporary being passed in and out of the 'pass' function and
eventually put into the 'bad' reference. But it *isn't*! The argument
of the 'pass' function and its return value are *different references*.
The return value is initialised from the argument initialised from the
temporary. So, if the rule was only about binding a ref to a temporary,
the temporary would only lives as long as the argument of the very first
'pass' function (the inner-most). The "until the full expression is
evaluated" requirement would override that in this case, so you should
see the temporary report its destruction right after the last 'pass'
returns, just before 'bad' is initialised.
How does it
make even sense that a reference can be created to an object which is
destroyed immediately after the reference is created?

The same way that a pointer can be created to an object that has already
been destroyed:

Object* foo(Object *p) {
delete p;
return p;
}

Any use of the return value of this 'foo' function will have undefined
behaviour.
I didn't quite understand this. Do you say that the call:
obj.Print(2);
is an undefined behavior?

Would this:
MyClass const& bad = pass(pass(pass( pass(MyClass()) .print(3))));
be UB as well?
Aug 22 '08 #5
anon wrote:
>>class MyClass
{
public:
MyClass() { std::cout << "constructor\n" ; }
~MyClass() { std::cout << "destructor \n"; }

const MyClass& print(int i) const
{
std::cout << i << std::endl;
return *this;
}
};
//---------------------------------------------------------
>>//---------------------------------------------------------
int main()
{
std::cout << "Before\n";
const MyClass& obj = MyClass(); //*
std::cout << "After\n";
obj.print(2);
}
//---------------------------------------------------------
>>>
MyClass getMyClass() { return MyClass(); }

Now if we change the line marked with //* to this:

const MyClass& obj = getMyClass(); //*

the result will still be the same. So clearly the lifetime of the return
value of a function is extended by the reference.

Now comes the puzzling part, and my actual question. Suppose that we
change the line marked with //* to this:

const MyClass& obj = MyClass().print (1); //*

Suddenly the output changes:

Before
constructor
1
destructor
After
2
[...]
>Imagine:

// using your 'MyClass' class
MyClass const& pass(MyClass const& arg) { return arg; }

MyClass const& bad = pass(pass(pass( pass(MyClass()) )));

Some might think that it is the same reference initialised by binding
it to the temporary being passed in and out of the 'pass' function and
eventually put into the 'bad' reference. But it *isn't*! The
argument of the 'pass' function and its return value are *different
references*. The return value is initialised from the argument
initialised from the temporary. So, if the rule was only about
binding a ref to a temporary, the temporary would only lives as long
as the argument of the very first 'pass' function (the inner-most).
The "until the full expression is evaluated" requirement would
override that in this case, so you should see the temporary report its
destruction right after the last 'pass' returns, just before 'bad' is
initialised.
> How does it
make even sense that a reference can be created to an object which is
destroyed immediately after the reference is created?

The same way that a pointer can be created to an object that has
already been destroyed:

Object* foo(Object *p) {
delete p;
return p;
}

Any use of the return value of this 'foo' function will have undefined
behaviour.

I didn't quite understand this. Do you say that the call:
obj.Print(2);
is an undefined behavior?
Well, it's actually

obj.print(2);

and, no. The return value of 'Print' is not being used in any way.
Would this:
MyClass const& bad = pass(pass(pass( pass(MyClass()) .print(3))));
be UB as well?
Nope. But any attempt to *use* 'bad' would.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Aug 22 '08 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

13
1851
by: John Harrison | last post by:
Rather a long program I'm afraid but I don't think I can cut it down any further. What I'm trying to do is construct a complex object Y from several X objects in a complex expression. I'm trying to do this without creating any temporaries of Y. To do that I'm defined a number of proxy classes which contain references to the arguments in the...
6
3658
by: Jason Heyes | last post by:
I am interested in the lifetime of a function argument in two cases. They are: 1. void foo(Bar bar); 2. void foo(const Bar &bar); In each case I call foo like so: foo(Bar());
8
2928
by: pt | last post by:
Hallo, i wonder how it is going to be of this code below regarding of the return of temporary object. Prototypes: =========== bool Activation(TCHAR *c); std::basic_string<TCHAR> GetFile();
15
4300
by: Gabor Drasny | last post by:
Hi all, Could anyone tell me if the following code is guaranteed to work or not? #include <string> #include <iostream> int main() { const char* s = std::string("Hello World").c_str();
3
1137
by: Bob Altman | last post by:
Hi all, I have a basic question regarding holding references to objects in unmanaged C++. Suppose my unmanaged C++ class has a method that accepts a reference to an object as an argument, and stores the object reference in a stack object, and another method that uses the stored objects in the stack object, like this: void...
14
4367
by: Frederick Gotham | last post by:
There is a common misconception, (one which I myself also held at one point), that a const reference can "extend the lifetime of a temporary". Examples such as the following are given: Snippet (1) ----------- #include <string> using std::string;
1
1747
by: n.torrey.pines | last post by:
http://www.artima.com/cppsource/foreach.html says "temporary objects of derived type can have their lifetime extended by binding them to a const reference to the base type." ==================================== #include <iostream> struct base {};
3
1556
by: mario semo | last post by:
Hello, What does the C++ Norm says about the lifetime of compiler generated temporary variables? #include <stdio.h> class BaseRef { //--------------------------------------------------------------------------
6
2673
by: better_cs_now | last post by:
Hello all, class Foo {/* Details don't matter */}; class Bar { public: Bar(): m_Foo(/* Construct a Foo however it wants to be constructed */); const Foo &GetFoo() const { return m_Foo; } private:
0
7701
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7615
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language...
1
7677
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For...
1
5514
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes...
0
5219
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert...
0
3643
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2115
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1223
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
940
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.