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 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/
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
"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
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/
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
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.
"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
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
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/
"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
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
"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/
"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 This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
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());
|
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();
|
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; }
};
|
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;
}
|
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...
|
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."
...
|
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...
|
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
{...
|
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; }...
|
by: Charles Arthur |
last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
|
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...
|
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
|
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...
|
by: nemocccc |
last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
|
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...
|
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...
|
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...
|
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...
| |