473,698 Members | 2,611 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Lifetime extension misconception


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;

#include <iostream>
using std::cout;

string Func()
{
return "Hello";
}

int main()
{
string const &str = Func();

cout << str << '\n';
}

In the above snippet, no temporary "has had its lifetime extended". If such a
thing were true, then the following code would be perfectly OK:

Snippet (2):
------------

#include <string>
using std::string;

string Func()
{
return "Hello";
}

int main()
{
string const &cstr = Func();

string &str = const_cast<stri ng&>(cstr);

str = "World";
}
But alas, Snippet (2) exhibits undefined behaviour, because the reference
does not refer to the original non-const object which was returned by value
from the function.

The code in Snippet (1) works on exactly the same principle as the following
code snippet:

Snippet (3):
------------

int main()
{
int const &r = 5;
}

In Snippet (1) and in Snippet (3), the const reference remains valid NOT
because a temporary has had its liftime extended, but because the reference
was initialised with an R-value. The C++ Standard defines this process:

If the initializer expression is an rvalue, with T2 a class type, and “cv1
T1” is reference-compatible with “cv2 T2,” the reference is bound in one of
the following ways (the choice is implementation-defined):
— The reference is bound to the object represented by the rvalue (see 3.10)
or to a sub-object within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called
to copy the entire rvalue object into the temporary. The reference is bound
to the temporary or to a sub-object within the temporary.

This explains why the following code snippet fails to compile, and disproves
that the lifetime of a temporary is extended by binding a const reference to
it.

Snippet (4):
------------

class MyClass {
private:
MyClass(MyClass const &); /* Can't copy-construct! */

public:
MyClass() {}
};

int main()
{
MyClass const &r = MyClass();
}

--

Frederick Gotham
Jul 29 '06 #1
14 4377
Frederick Gotham wrote:
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;

#include <iostream>
using std::cout;

string Func()
{
return "Hello";
}

int main()
{
string const &str = Func();

cout << str << '\n';
}

In the above snippet, no temporary "has had its lifetime extended".
If such a thing were true, then the following code would be perfectly
OK:

Snippet (2):
------------

#include <string>
using std::string;

string Func()
{
return "Hello";
}

int main()
{
string const &cstr = Func();

string &str = const_cast<stri ng&>(cstr);

str = "World";
}
But alas, Snippet (2) exhibits undefined behaviour, because the
reference does not refer to the original non-const object which was
returned by value from the function.

The code in Snippet (1) works on exactly the same principle as the
following code snippet:

Snippet (3):
------------

int main()
{
int const &r = 5;
}

In Snippet (1) and in Snippet (3), the const reference remains valid
NOT because a temporary has had its liftime extended, but because the
reference was initialised with an R-value. The C++ Standard defines
this process:

If the initializer expression is an rvalue, with T2 a class type, and
“cv1 T1” is reference-compatible with “cv2 T2,” the reference is
bound in one of the following ways (the choice is
implementation-defined):
— The reference is bound to the object represented by the rvalue (see
3.10) or to a sub-object within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is
called to copy the entire rvalue object into the temporary. The
reference is bound to the temporary or to a sub-object within the
temporary.

This explains why the following code snippet fails to compile, and
disproves that the lifetime of a temporary is extended by binding a
const reference to it.

Snippet (4):
------------

class MyClass {
private:
MyClass(MyClass const &); /* Can't copy-construct! */

public:
MyClass() {}
};

int main()
{
MyClass const &r = MyClass();
}
Here is another interesting example:

char const* hello() { return "Hello"; }

#include <string>
#include <iostream>

int main() {
std::string const& str = hello();
std::cout << str << std::endl;
}

The temporary is not returned from anywhere, it's created as the result
of the initialisation of the reference. There is no lifetime "extention" ,
only the lifetime of the temporary to which the reference is bound. If
it is the original temporary, you can think of it as "extention" of its
lifetime, but the point you made with your private copy-c-tor constructor
is that it doesn't have to be the original temporary. It could just as
well be *another* temporary, copy-constructed from the "original" or from
something else, like in my example.

Just like in any other situation with returning by value, the copy-c-tor
has to be available for the code to be legal, even if copying is not done.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 29 '06 #2
Frederick Gotham wrote:
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".
That is not a misconception.
#include <string>
using std::string;

#include <iostream>
using std::cout;

string Func()
{
return "Hello";
}

int main()
{
string const &str = Func();

cout << str << '\n';
}

In the above snippet, no temporary "has had its lifetime extended".
The return value of Func() is a temporary. It has its lifetime extended
by being bound to 'str'.

If it did not have its lifetime extended, then the cout << str would be
referencing an object that no longer existed.
If such a thing were true, then the following code would be perfectly OK:

Snippet (2):
------------

#include <string>
using std::string;

string Func()
{
return "Hello";
}

int main()
{
string const &cstr = Func();

string &str = const_cast<stri ng&>(cstr);

str = "World";
}
yes, that code is OK.
But alas, Snippet (2) exhibits undefined behaviour,
No it doesn't.
because the reference does not refer to the original non-const object
which was returned by value from the function.
The reference refers to the return value of the function, not the
original object that was returned by value, as you say.
The code in Snippet (1) works on exactly the same principle as the following
code snippet:

Snippet (3):
------------

int main()
{
int const &r = 5;
}

In Snippet (1) and in Snippet (3), the const reference remains valid NOT
because a temporary has had its liftime extended,
That code creates a temporary int , initializes it to 5, and then
binds 'r' to it. The temporary has its lifetime extended to the
scope of 'r'.
but because the reference was initialised with an R-value.
The C++ Standard defines this process:

- A temporary of type "cv1 T2" [sic] is created, and a constructor is called
to copy the entire rvalue object into the temporary. The reference is bound
to the temporary or to a sub-object within the temporary.
Yes, that is what happened. A temporary of type "int" was created,
and its "copy constructor" was used to initialize it with 5. The
reference
is then bound to the temporary, and that temporary's lifetime is
extended.

I put "copy constructor" in quotes because ints don't really have copy
constructors. The string example is more straight forward.
This explains why the following code snippet fails to compile
and disproves that the lifetime of a temporary is extended by binding
a const reference to it.
It does no such thing.
>
Snippet (4):
------------

class MyClass {
private:
MyClass(MyClass const &); /* Can't copy-construct! */

public:
MyClass() {}
};

int main()
{
MyClass const &r = MyClass();
}
This fails to compile because the standard explicitly says that a
copy constructor is needed when initializing a reference with
a temporary. In fact you quoted that section of the standard earlier.

In this whole thread, I think you are confusing the object being
returned, with the return value. The return value (a temporary)
exists in the scope of the calling function, and is copy-constructed
from the object being returned (which exists in the scope of the
called function).

Normally the return value is destroyed at the end of the full-
expression, since it is a temporary. But when it is bound to
a const reference, it is not destroyed.

Jul 31 '06 #3
Old Wolf posted:
>But alas, Snippet (2) exhibits undefined behaviour,

No it doesn't.

Read the rules about binding a const reference to an R-value:

- A temporary of type "cv1 T2" [sic] is created, and a constructor is called
to copy the entire rvalue object into the temporary. The reference is bound
to the temporary or to a sub-object within the temporary.

Take note of the fifth word in the above paragraph -- "cv1".

Code Snippet (2) invokes Undefined Behaviour.

--

Frederick Gotham
Jul 31 '06 #4
Frederick Gotham wrote:
Old Wolf posted:
But alas, Snippet (2) exhibits undefined behaviour,
No it doesn't.

Read the rules about binding a const reference to an R-value:

- A temporary of type "cv1 T2" [sic] is created, and a constructor is called
to copy the entire rvalue object into the temporary. The reference is bound
to the temporary or to a sub-object within the temporary.
Incidentally, this quote disproves your entire position: it says
clearly that a temporary is created, and the reference is bound to it.

The temporary continues to exist after the end of the declaration,
ie. it has its lifetime extended.
Take note of the fifth word in the above paragraph -- "cv1".
What about it?
Code Snippet (2) invokes Undefined Behaviour.
It doesn't. Here's the code again:

string const &cstr = Func();
string &str = const_cast<stri ng&>(cstr);

The initialization of 'str' is covered by this part of 8.5.3#5:

A reference to type "cv1 T1" is initialized by an expression of
type "cv2 T2" as follows:
If the initializer expression is an lvalue (but is not a bitfield),
and "cv1 T1" is reference-compatible with "cv2 T2," or
[other case snipped], then the reference is bound directly
to the initializer expression lvalue

The initializer expression is "const_cast<str ing &>(cstr)", which
is an lvalue (*). cv1 and cv2 are both nothing, T1 is
"string" and T2 is "string&", so "cv1 T1" and "cv2 T2" are
reference-compatible (which is explained in 8.5.3).

So this section seems to covers the initialization.

Can you find any compiler that can't handle Snippet 2? gcc has
no problem with it.
(*) 5.2.11:
The result of the expression const_cast<T>(v ) is of type T.
If T is a reference type, the result is an lvalue;

Jul 31 '06 #5

Old Wolf wrote:
string const &cstr = Func();
string &str = const_cast<stri ng&>(cstr);
Now, at this point is modifying the object through str defined or no?

Aug 1 '06 #6
Old Wolf wrote:
Frederick Gotham wrote:
>Old Wolf posted:
>But alas, Snippet (2) exhibits undefined behaviour,

No it doesn't.
I am afraid it does. I don't like it, but I can see the point.

>Read the rules about binding a const reference to an R-value:

- A temporary of type "cv1 T2" [sic] is created, and a constructor is
called to copy the entire rvalue object into the temporary. The reference
is bound to the temporary or to a sub-object within the temporary.

Incidentally, this quote disproves your entire position: it says
clearly that a temporary is created, and the reference is bound to it.

The temporary continues to exist after the end of the declaration,
ie. it has its lifetime extended.
>Take note of the fifth word in the above paragraph -- "cv1".

What about it?
We shall see below.
>Code Snippet (2) invokes Undefined Behaviour.

It doesn't. Here's the code again:

string const &cstr = Func();
string &str = const_cast<stri ng&>(cstr);

The initialization of 'str' is covered by this part of 8.5.3#5:
But the important part is the initialization of cstr: The compiler is free
to initialize it as follows:

a) a temporary X of type const string (this is where the cv1 kicks in!) is
created and initialized from the return value of Func().

b) this temporary (of type const string) is bound to cstr (and if you
insist, has it's lifetime extended).
Now, when you do the const_cast, you are casting away the constness of X
which has type const string. Any attempt to modify the value will then be
undefined behavior.
>
A reference to type "cv1 T1" is initialized by an expression of
type "cv2 T2" as follows:
If the initializer expression is an lvalue (but is not a bitfield),
and "cv1 T1" is reference-compatible with "cv2 T2," or
[other case snipped], then the reference is bound directly
to the initializer expression lvalue

The initializer expression is "const_cast<str ing &>(cstr)", which
is an lvalue (*). cv1 and cv2 are both nothing, T1 is
"string" and T2 is "string&", so "cv1 T1" and "cv2 T2" are
reference-compatible (which is explained in 8.5.3).

So this section seems to covers the initialization.

Can you find any compiler that can't handle Snippet 2? gcc has
no problem with it.
Nope, and I think it is a defect in the standard.
>
(*) 5.2.11:
The result of the expression const_cast<T>(v ) is of type T.
If T is a reference type, the result is an lvalue;


Best

Kai-Uwe Bux
Aug 1 '06 #7
Kai-Uwe Bux wrote:
Old Wolf wrote:
>Frederick Gotham wrote:
>>>
- A temporary of type "cv1 T2" [sic] is created, and a constructor is
called to copy the entire rvalue object into the temporary. The reference
is bound to the temporary or to a sub-object within the temporary.
Here's the code again:

string const &cstr = Func();
string &str = const_cast<stri ng&>(cstr);

But the important part is the initialization of cstr: The compiler is free
to initialize it as follows:

a) a temporary X of type const string (this is where the cv1 kicks in!) is
created and initialized from the return value of Func().

b) this temporary (of type const string) is bound to cstr

Now, when you do the const_cast, you are casting away the constness of X
which has type const string. Any attempt to modify the value will then be
undefined behavior.
Good explanation.
>Can you find any compiler that can't handle Snippet 2? gcc has
no problem with it.

Nope, and I think it is a defect in the standard.
Well, the standard says that the compiler has two options:
1) Bind the reference to the (non-const) return value
2) Bind the reference to a "cv1 T2" temporary constructed
from the return value.

So all these compilers that work, could simply be choosing option 1.

But it would seem to make more sense if the constructed temporary
had type "cv2 T2". Why does the standard say "[sic]" in it?

Also interesting is the earlier part of the same section that says that
if "cv2 T2" can be implicitly converted to an lvalue of type "cv3 t3",
then this conversion must be performed and bound to the reference:

A reference to type "cv1 T1" is initialized by an expression of
type "cv2 T2" as follows: If the initializer expression
- has a class type (i.e., T2 is a class type) and can be
implicitly converted to an lvalue of type "cv3 T3," where
"cv1 T1" is reference-compatible with "cv3 T3" 92)
(this conversion is selected by enumerating the
applicable conversion functions (13.3.1.6) and choosing
the best one through overload resolution (13.3)),

then the reference is bound directly to ... the lvalue result of the
conversion in the second case.

If we choose "cv3 t3" = "std::strin g", does that fit the criteria? A
std::string rvalue can be converted to a std::string lvalue by using
the copy-constructor!

Aug 1 '06 #8
Frederick Gotham wrote:
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)
-----------
...
string Func()
{
return "Hello";
}

int main()
{
string const &str = Func();

cout << str << '\n';
}

In the above snippet, no temporary "has had its lifetime extended".
That's not correct. There's no definitive answer to what exactly happens there,
because it depends on implementation-defined behavior. The implementation is
free to choose one of two ways to initialize the reference: either 1) bind it
directly to the temporary object returned by 'Func', thus extending its
lifetime, or 2) create another temporary object of type 'const string' (by
copying the original temporary) and bind the reference to it, thus, again,
extending its lifetime. (The implementation is also free to apply the second
method repeatedly, until it eventually chooses the first method.)

Regardless of the method chosen by implementation, we have the lifetime of some
temporary extended here.
If such a
thing were true, then the following code would be perfectly OK:

Snippet (2):
------------
...
string Func()
{
return "Hello";
}

int main()
{
string const &cstr = Func();

string &str = const_cast<stri ng&>(cstr);

str = "World";
}
But alas, Snippet (2) exhibits undefined behaviour, because the reference
does not refer to the original non-const object which was returned by value
from the function.
Incorrect. _In_ _general_ _case_, the reference indeed does not refer to the
original object. _In_ _general_ _case_ it is possible that the reference is
actually bound to a 'const string' object, which means that the code indeed
produces undefined behavior _in_ _general_ _case_.

However, if a concrete implementation defines its behavior so that the reference
is always bound to the original object returned by 'Func', then the code is
perfectly fine within the bounds of that implementation.
The code in Snippet (1) works on exactly the same principle as the following
code snippet:

Snippet (3):
------------

int main()
{
int const &r = 5;
}
It _might_ work on exactly the same principle. The standard separated the cases
of class types and non-class types in 8.5.3/5 for a reason: there are
differences between reference initialization rules in those cases.
In Snippet (1) and in Snippet (3), the const reference remains valid NOT
because a temporary has had its liftime extended, but because the reference
was initialised with an R-value.
Huh? Initializing a reference with an rvalue always results in some temporary
having its lifetime extended. How can you contrapose these two concepts?

The C++ Standard defines this process:
>
If the initializer expression is an rvalue, with T2 a class type, and “cv1
T1” is reference-compatible with “cv2 T2,” the reference is bound in one of
the following ways (the choice is implementation-defined):
— The reference is bound to the object represented by the rvalue (see 3.10)
or to a sub-object within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called
to copy the entire rvalue object into the temporary. The reference is bound
to the temporary or to a sub-object within the temporary.

This explains why the following code snippet fails to compile, and disproves
that the lifetime of a temporary is extended by binding a const reference to
it.

Snippet (4):
------------

class MyClass {
private:
MyClass(MyClass const &); /* Can't copy-construct! */

public:
MyClass() {}
};

int main()
{
MyClass const &r = MyClass();
}
This doesn't disprove anything. In all "snippets" you provided so far some
temporary always has its lifetime extended. It might be completely different
temporary, not exactly the one you originally expected, but there;'s always one
that gets an extension.

--
Best regards,
Andrey Tarasevich
Aug 1 '06 #9
Old Wolf wrote:
...
Also interesting is the earlier part of the same section that says that
if "cv2 T2" can be implicitly converted to an lvalue of type "cv3 t3",
then this conversion must be performed and bound to the reference:

A reference to type "cv1 T1" is initialized by an expression of
type "cv2 T2" as follows: If the initializer expression
- has a class type (i.e., T2 is a class type) and can be
implicitly converted to an lvalue of type "cv3 T3," where
"cv1 T1" is reference-compatible with "cv3 T3" 92)
(this conversion is selected by enumerating the
applicable conversion functions (13.3.1.6) and choosing
the best one through overload resolution (13.3)),

then the reference is bound directly to ... the lvalue result of the
conversion in the second case.

If we choose "cv3 t3" = "std::strin g", does that fit the criteria? A
std::string rvalue can be converted to a std::string lvalue by using
the copy-constructor!
I don't see how you can perform such a conversion using a copy constructor.
Remember that temporary objects are not lvalues (as seen from "outside"). No,
the only way to convert an rvalue of class type to an lvalue is a user-defined
conversion operator returning a reference (which is mentioned in footnote 92).

--
Best regards,
Andrey Tarasevich
Aug 1 '06 #10

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

Similar topics

13
1862
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 expression. All the proxies define a conversion operator to Y which will ultimately be used to...
6
3676
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
2938
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();
0
1134
by: JD | last post by:
I'm currently writing a class to manage per-user configuration files, using the same format as the app.config file. This will be used in a Winforms app to manage user options, window positions, etc. When instantiated, the object will be available as a service at the application level. The question I have is about the most efficient way to manage the lifetime of the XmlDocument object that contains the XML file itself. Currently, I do...
1
1753
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
2339
by: nagashre | last post by:
class A { public: A():a(0), b(0){} handleMyMsg( char* aa, char*bb); private: processMessage();
3
1563
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
2695
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:
5
1478
by: Juha Nieminen | last post by:
Let's assume we have a class like this: //--------------------------------------------------------- #include <iostream> class MyClass { public: MyClass() { std::cout << "constructor\n"; } ~MyClass() { std::cout << "destructor\n"; }
0
9170
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9031
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
6528
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 instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5862
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 into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4372
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4623
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3052
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
2
2339
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2007
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 effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.