473,396 Members | 1,996 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

lifetime of temporaries

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 create
the Y object. The issue for me is whether I'm guaranteed that all the
temporaries will still exist when the conversion happens. Here's the code

#include <iostream>
using namespace std;

int gen = 0;

class X
{
};

class Y
{
};

class Proxy
{
public:
Proxy() : id(gen++) {}
Proxy(const Proxy&) : id(gen++) {}
virtual ~Proxy() {}
operator Y()
{
cout << "convert\n";
return Y();
}
protected:
int id;
};

class XX_proxy : public Proxy
{
public:
XX_proxy(const X& l, const X& r) : left(l), right(r) { cout << "XX" << id
<< '\n'; }
~XX_proxy() { cout << "~XX" << id << '\n'; }
private:
const X& left;
const X& right;
};

class AX_proxy : public Proxy
{
public:
AX_proxy(const Proxy& l, const X& r) : left(l), right(r) { cout << "AX" <<
id << '\n'; }
~AX_proxy() { cout << "~AX" << id << '\n'; }
private:
const Proxy& left;
const X& right;
};

class XA_proxy : public Proxy
{
public:
XA_proxy(const X& l, const Proxy& r) : left(l), right(r) { cout << "XA" <<
id << '\n'; }
~XA_proxy() { cout << "~XA" << id << '\n'; }
private:
const X& left;
const Proxy& right;
};

class AA_proxy : public Proxy
{
public:
AA_proxy(const Proxy& l, const Proxy& r) : left(l), right(r) { cout << "AA"
<< id << '\n'; }
~AA_proxy() { cout << "~AA" << id << '\n'; }
private:
const Proxy& left;
const Proxy& right;
};

XX_proxy operator|(const X& l, const X& r)
{
return XX_proxy(l, r);
}

AX_proxy operator|(const Proxy& l, const X& r)
{
return AX_proxy(l, r);
}

XA_proxy operator|(const X& l, const Proxy& r)
{
return XA_proxy(l, r);
}

AA_proxy operator|(const Proxy& l, const Proxy& r)
{
return AA_proxy(l, r);
}

int main()
{
X x1, x2, x3, x4;
Y y = x1 | x2 | x3 | x4;
}

The following program outputs

XX0
AX1
AX2
convert
~AX2
~AX1
~XX0

on the two compilers I've tested it on, which is good, "convert" is output
before any of the destructors are called. But I'm not sure if this is
guaranteed behaviour. I've tried reading the standard but its a bit opaque
to me.

Thanks,
John
Jul 22 '05 #1
13 1841
John Harrison wrote in news:c6************@ID-196037.news.uni-berlin.de
in comp.lang.c++:

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 create the Y object. The issue for me is
whether I'm guaranteed that all the temporaries will still exist when
the conversion happens. Here's the code

[snip expression template (without the template) example]

The temporaries are destroyed at the final sequence point of the
full expresion, that's the ';', after the initialization of Y y,
so your code is ok.

What will happen when/if we get the auto extension and can rewrite:
int main()
{
X x1, x2, x3, x 4;
Y y = x1 | x2 | x3 | x4;
}


as

int main()
{
X x1, x2, x3, x 4;

auto e1 = x1 | x2;
auto e2 = x3 | x4;

Y y = e1 | e2;
}

Is another matter. I've a feeling it just won't scale :),
though the above example should be ok.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #2
Rob Williscroft wrote:

What will happen when/if we get the auto extension and can rewrite:

int main()
{
X x1, x2, x3, x 4;
Y y = x1 | x2 | x3 | x4;
}

as

int main()
{
X x1, x2, x3, x 4;

auto e1 = x1 | x2;
auto e2 = x3 | x4;

Y y = e1 | e2;
}

Is another matter. I've a feeling it just won't scale :),
though the above example should be ok.


According to the C++ standard the lifecycle of a temporary may be even
longer than the end of the full-expression when it is used in an
expression initializing an object or is assigned to a reference.

I think it might be quite hard to write code that bites with a
disappearing temporary as far as a standard conforming compiler is used.
Regards,
Janusz
Jul 22 '05 #3

"Janusz Szpilewski" <sz******@poczta.onet.pl> wrote in message
news:c6**********@news.onet.pl...
Rob Williscroft wrote:

What will happen when/if we get the auto extension and can rewrite:

int main()
{
X x1, x2, x3, x 4;
Y y = x1 | x2 | x3 | x4;
}

as

int main()
{
X x1, x2, x3, x 4;

auto e1 = x1 | x2;
auto e2 = x3 | x4;

Y y = e1 | e2;
}

Is another matter. I've a feeling it just won't scale :),
though the above example should be ok.


According to the C++ standard the lifecycle of a temporary may be even
longer than the end of the full-expression when it is used in an
expression initializing an object or is assigned to a reference.

I think it might be quite hard to write code that bites with a
disappearing temporary as far as a standard conforming compiler is used.
Regards,
Janusz


I managed it with my first attempt at this. That had code similar to this

class X {};
class Y { Y(const X& a) : arg(a) {} const X& arg; };
class Z { Z(const Y& l, const Y& r) : left(l), right(r) {} const Y& left;
const Y& right; };

Z func(const X& l, const X& r)
{
return Z(Y(l), Y(r));
}

X x1, x2;
Z z = func(x1, x2);

The Y temporaries got destroyed while the Z object still existed.

john
Jul 22 '05 #4
Janusz Szpilewski wrote in news:c6**********@news.onet.pl in comp.lang.c++:
Rob Williscroft wrote:

What will happen when/if we get the auto extension and can rewrite:

int main()
{
X x1, x2, x3, x 4;
Y y = x1 | x2 | x3 | x4;
}

as

int main()
{
X x1, x2, x3, x 4;

auto e1 = x1 | x2;
auto e2 = x3 | x4;

Y y = e1 | e2;
}

Is another matter. I've a feeling it just won't scale :),
though the above example should be ok.


According to the C++ standard the lifecycle of a temporary may be even
longer than the end of the full-expression when it is used in an
expression initializing an object or is assigned to a reference.

I think it might be quite hard to write code that bites with a
disappearing temporary as far as a standard conforming compiler
is used.


I wish you were right:

#include <iostream>
#include <ostream>

using namespace std;

struct X
{
X() { cout << "X()\n"; }
X( X const & ) { cout << "X(X const &)\n"; }
~X() { cout << "~X()\n"; }
};
struct Y
{
Y( X const &x ) : xp( &x ) { cout << "Y()\n"; }
Y( Y const &rhs ) : xp( rhs.xp ) { cout << "Y(Y const &)\n"; }
~Y() { cout << "~Y()\n"; }

X const *xp;
};
void f()
{
Y y( (X()) );

cout << "f()\n";
}

int main()
{
f();
cout.flush();
}
Every compiler I tried gave:

X()
Y()
~X()
f()
~Y()

So either they are all wrong (*), or that "...initializing an object..."
stuff isn't about keeping the X() from Y y( (X()) ); around.

*) Not impossible, compilers were:

MSVC 7.1, g++ 3.4 (prerelease) and CBuildeX (preview/EDG))

Consider:

X x1 = X(), x2 = X();

Note that the first X() gets consumed ( in effect becomes x1 )
by copy-ctor elision, so doesn't survive to the ';', but this is
2 initialization's, is it 1 (full) expression ?

So I really don't get that "initializing an object" stuff. All
I can think is it means when a temporary becomes the object
its initializing, so in effect the X() initializing x1 above
becomes x1, and thus 'lives' beyond the full expression.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #5
Rob Williscroft wrote:

So I really don't get that "initializing an object" stuff. All
I can think is it means when a temporary becomes the object
its initializing, so in effect the X() initializing x1 above
becomes x1, and thus 'lives' beyond the full expression.


Some qoute from the standard (draft version):

" (..) The first context is when an expression appears as an initializer
for a declarator defining an object. In that context, the temporary
that holds the result of the expression shall persist until the object's
initialization is complete. "

It means that the temporary should be destroyed after the object
initialization, hence just after leaving the constructor. So it does not
have to live as long as the constructed object.

Regards,
Janusz
Jul 22 '05 #6
John Harrison wrote:
I managed it with my first attempt at this. That had code similar to this

class X {};
class Y { Y(const X& a) : arg(a) {} const X& arg; };
class Z { Z(const Y& l, const Y& r) : left(l), right(r) {} const Y& left;
const Y& right; };

Z func(const X& l, const X& r)
{
return Z(Y(l), Y(r));
}

X x1, x2;
Z z = func(x1, x2);

The Y temporaries got destroyed while the Z object still existed.


Yes, if a temporary is bound to a reference member it will persist only
until constructor ends.

Jul 22 '05 #7

"Janusz Szpilewski" <sz******@poczta.onet.pl> wrote in message
news:c6**********@news.onet.pl...
John Harrison wrote:
I managed it with my first attempt at this. That had code similar to this
class X {};
class Y { Y(const X& a) : arg(a) {} const X& arg; };
class Z { Z(const Y& l, const Y& r) : left(l), right(r) {} const Y& left; const Y& right; };

Z func(const X& l, const X& r)
{
return Z(Y(l), Y(r));
}

X x1, x2;
Z z = func(x1, x2);

The Y temporaries got destroyed while the Z object still existed.


Yes, if a temporary is bound to a reference member it will persist only
until constructor ends.


But doesn't that apply to my other code as well? In that code I created an
XX_proxy object which was bound to a reference member (in a AX_proxy object)
yet that persisted beyond the end of the AX_proxy constructor. This is
essentially the point that confused me when I tried to read the standard.

John
Jul 22 '05 #8
John Harrison wrote:
"Janusz Szpilewski" <sz******@poczta.onet.pl> wrote in message
news:c6**********@news.onet.pl...
John Harrison wrote:

I managed it with my first attempt at this. That had code similar to
this
class X {};
class Y { Y(const X& a) : arg(a) {} const X& arg; };
class Z { Z(const Y& l, const Y& r) : left(l), right(r) {} const Y&
left;
const Y& right; };

Z func(const X& l, const X& r)
{
return Z(Y(l), Y(r));
}

X x1, x2;
Z z = func(x1, x2);

The Y temporaries got destroyed while the Z object still existed.


Yes, if a temporary is bound to a reference member it will persist only
until constructor ends.

But doesn't that apply to my other code as well? In that code I created an
XX_proxy object which was bound to a reference member (in a AX_proxy object)
yet that persisted beyond the end of the AX_proxy constructor. This is
essentially the point that confused me when I tried to read the standard.

John


In the code posted previously the temporaries primarily existed within
the context of the full-expression where they were created (y = x1 | x2
| x3 | x4). In the latter case when 'func' returns the Z member
reference seems to be the only contexts of the existence of the
temporary what is not enough after the completion of the object
construction.

Regards,
Janusz
Jul 22 '05 #9
Janusz Szpilewski wrote in news:c6**********@news.onet.pl in comp.lang.c++:
Rob Williscroft wrote:

So I really don't get that "initializing an object" stuff. All
I can think is it means when a temporary becomes the object
its initializing, so in effect the X() initializing x1 above
becomes x1, and thus 'lives' beyond the full expression.

Some qoute from the standard (draft version):

" (..) The first context is when an expression appears as an initializer
for a declarator defining an object. In that context, the temporary
that holds the result of the expression shall persist until the object's
initialization is complete. "

It means that the temporary should be destroyed after the object
initialization, hence just after leaving the constructor. So it does not
have to live as long as the constructed object.


Yup, but that doesn't explain how, due to a temporary being
used in an intialization, the temporary can persist beyond the
full expression.

However I just searched the current Standard for 'temporary'
and I couldn't find a reference to:
According to the C++ standard the lifecycle of a temporary may be even
longer than the end of the full-expression when it is used in an
expression initializing an object or is assigned to a reference.


or similar, except for binding to T const & and in throw expression's.

So it seems my memory is at fault and temporaries are destroyed at
the end of "the full expression", except in the two cases noted
above.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #10
"John Harrison" <jo*************@hotmail.com> wrote in message news:<c6************@ID-196037.news.uni-berlin.de>...
Rather a long program I'm afraid but I don't think I can cut it down any
further.

*snip*
Y y = x1 | x2 | x3 | x4;


Well, fortunately the answer is short .. <grin> Yes, the temporaries
returned by the operator| calls are guaranteed to live until the end
of the full expression where they are generated .. in this case the
assignment expression quoted above.

Dave Moore
Jul 22 '05 #11
Rob Williscroft wrote:

Yup, but that doesn't explain how, due to a temporary being
used in an intialization, the temporary can persist beyond the
full expression.

However I just searched the current Standard for 'temporary'
and I couldn't find a reference to:

According to the C++ standard the lifecycle of a temporary may be even
longer than the end of the full-expression when it is used in an
expression initializing an object or is assigned to a reference.

or similar, except for binding to T const & and in throw expression's.

So it seems my memory is at fault and temporaries are destroyed at
the end of "the full expression", except in the two cases noted
above.


When I wrote "longer" I primarily kept in mind the case of binding
temporary to a reference. Object initialization concerns more formal
issue as the formal name of an construct defining an object (containing
a type name) is called declarator and it covers more than just an
expression (which represents the initializer part). So the standard had
just to clarify this issue.

Regards,
Janusz

Jul 22 '05 #12
"John Harrison" <jo*************@hotmail.com> wrote in message
news:c6************@ID-196037.news.uni-berlin.de...
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.


[snip]

I hope your objective of avoiding temporaries is worth writing code which
does so little with so much complexity. To put it more bluntly, I can hardly
think of a practical application where the increased efficiency would
justify such obscure code.

Still, it would be nice to be able to control creation and destruction of
temporaries. Maybe it's a problem with the language.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #13
"John Harrison" <jo*************@hotmail.com> wrote in message news:<c6************@ID-196037.news.uni-berlin.de>...

I managed it with my first attempt at this. That had code similar to this

class X {};
class Y { Y(const X& a) : arg(a) {} const X& arg; };
class Z { Z(const Y& l, const Y& r) : left(l), right(r) {} const Y& left;
const Y& right; };

Z func(const X& l, const X& r)
{
return Z(Y(l), Y(r));
}

X x1, x2;
Z z = func(x1, x2);

The Y temporaries got destroyed while the Z object still existed.


I think that's special because RVO should be applied here:
there is only one Z object with lifetime geater than func().
It's like saying

X x1, x2;
Z z (Y(x1), Y(x2));

where, too, the Y temporaries get destroyed while the Z still exists.

Gerald
Jul 22 '05 #14

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

Similar topics

6
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());
15
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();
1
by: Neelesh Bodas | last post by:
Hello all, Please consider this code : class X { int x; public: X(int p ) : x(p) { } operator int() { x = 1; return x; } };
3
by: bb | last post by:
Hi, Have a query regarding the life of temporaries. Here is the code... class MyNumber { public: MyNumber(int n) : n(n) { cout << "Object Constructed." << endl; }
12
by: dave_dp | last post by:
Hi, I have just started learning C++ language.. I've read much even tried to understand the way standard says but still can't get the grasp of that concept. When parameters are passed/returned...
1
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." ...
13
by: dragoncoder | last post by:
Hi everyone, please consider the following function:- const int& foo ( const double& d ) { return d; } g++ compiles it with warnings and solaris CC gives error. I want to know if the code...
3
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
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; }...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
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...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

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.