 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Bronek Kozicki Guest
|
Posted: Sun Oct 24, 2004 4:02 am Post subject: unanswered question about clause 8.5.3/5 |
|
|
This question has been asked on comp.std.c++ week ago, no answer yet.
Here's relevant part of the C++ standard, clause 8.5.3/5:
---- citation begin
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):
[...]
- 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.
The constructor that would be used to make the copy shall be callable
whether or not the copy is actually done.
---- citation end
Effectively rvalue of non-copyable class cannot be bound to const
reference. Thus following code is ill-formed:
class C
{
C(const C&);
public:
C(){}
};
void g(const C&) {}
int main()
{
g(C());
}
..... as well as following one:
class D
{
D(const D&);
friend D f();
public:
D(){}
};
D f() {return D();}
int main()
{
const D& d = f();
}
Some compilers seem to not implement this clause, ie. will compile at
least one of above code samples without error. These compilers are GCC
3.3.1 (regardless of compilation options), MSVC (all versions, with
"language extensions" enabled), Comeau (in non-strict mode), and
possibly more.
What's purpose of this clause in the C++ standard? Under what scenarios
it's useful enough to justify cost?
B.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Rob Williscroft Guest
|
Posted: Sun Oct 24, 2004 2:42 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Bronek Kozicki wrote in
news:872cd$417a14ef$3e79757c$5254 (AT) nf1 (DOT) news-service-com in
comp.lang.c++.moderated:
| Quote: | This question has been asked on comp.std.c++ week ago, no answer yet.
Here's relevant part of the C++ standard, clause 8.5.3/5:
---- citation begin
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):
[...]
- 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.
The constructor that would be used to make the copy shall be callable
whether or not the copy is actually done.
---- citation end
Effectively rvalue of non-copyable class cannot be bound to const
reference.
|
The thing about rvalue's is they aren't like other object's,
int f() { return 0; }
f() return's an rvalue and on most platform's this rvalue will
be in a register, so to bind this to a int const & a copy *must*
be made.
This could apply to any type with a purely compiler generated
copy constructor, say both of:
struct A { int a, b; };
struct B : A { int c; };
Or, for sufficiently smart compilers, any type with a purely
compiler generated or inline (or templated) copy constructors.
Rob.
--
http://www.victim-prime.dsl.pipex.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Andrew Koenig Guest
|
Posted: Mon Oct 25, 2004 4:36 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
"Bronek Kozicki" <brok (AT) rubikon (DOT) pl> wrote
| Quote: | What's purpose of this clause in the C++ standard? Under what scenarios
it's useful enough to justify cost?
|
The purpose is to reduce the chance that code that compiles on one compiler
will fail to compile on another. Such situations make testing difficult,
because if your only compiler accepts a program, there is no easy way of
determining that another compiler will reject it.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Bronek Kozicki Guest
|
Posted: Mon Oct 25, 2004 7:54 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Rob Williscroft wrote:
| Quote: | int f() { return 0; }
f() return's an rvalue and on most platform's this rvalue will
be in a register, so to bind this to a int const & a copy *must*
be made.
|
I guess you are talking about copy of return value. Do we need this copy
to be made *again* when binding return value to const reference? I do
not see any reason for that. And I do not want to enter compiler
optimization ground, as it is allowed to do anything under "as if" rule.
| Quote: | This could apply to any type with a purely compiler generated
copy constructor, say both of:
|
copy constructor implicitly generated by compiler is always public -
again, I do not see any problem here. However, I still see problem that
I do not understand rationale behind clause cited in my post.
B.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
johnchx Guest
|
Posted: Tue Oct 26, 2004 1:26 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Rob Williscroft <rtw (AT) freenet (DOT) co.uk> wrote
| Quote: | The thing about rvalue's is they aren't like other object's,
int f() { return 0; }
f() return's an rvalue and on most platform's this rvalue will
be in a register, so to bind this to a int const & a copy *must*
be made.
This could apply to any type with a purely compiler generated
copy constructor, say both of:
struct A { int a, b; };
struct B : A { int c; };
Or, for sufficiently smart compilers, any type with a purely
compiler generated or inline (or templated) copy constructors.
|
A rationale along these lines occured to me too, but I don't think it
actually holds water. Two problems:
(1) An rvalue of class type always denotes an object (3.10/2) and an
object is a region of storage (1.8/1). Only an rvalue of built-in
type is allowed to denote a value which is not an object (i.e. which
exists only in a register). A compiler might be able to return a
value of class type in a register as an optimization under the as-if
rule, but only if it can do so without affecting the semantics of the
program.
(2) It is possible to call a non-static member function of a class via
an rvalue, without copying the object denoted by the rvalue. That
means that it is always possible to obtain the object's address
(supplied as the this pointer) without invoking the copy constructor
(or requiring that it be accessible).
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Rob Williscroft Guest
|
Posted: Tue Oct 26, 2004 1:28 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Bronek Kozicki wrote in news:ec5c6$417bf369$3e79757c$9291 (AT) nf1 (DOT) news-service-
com in comp.lang.c++.moderated:
| Quote: | Rob Williscroft wrote:
int f() { return 0; }
f() return's an rvalue and on most platform's this rvalue will
be in a register, so to bind this to a int const & a copy *must*
be made.
I guess you are talking about copy of return value. Do we need this
copy to be made *again*
|
There is no *again* the rvalue is normally "copied" into a register by
the called function and is used by the caller still in the register.
i.e. no copy is made by the caller except when it needs to do an rvalue
to lvalue conversion.
| Quote: | when binding return value to const reference?
I do not see any reason for that. And I do not want to enter compiler
optimization ground, as it is allowed to do anything under "as if"
rule.
|
Yes, but they don't as they may not have enough information to
work out all the what-if's. If language rules can reduce the
number of what-if's then more compilers will produce high quality
optimized code.
Example TU:
struct X
{
int a;
X( X const & ); /* defined in *another* TU */
};
X x = X();
The as-if rule isn't enough to optimize away the copy constructor
in the above, because the compiler has no idea what the copy
constructor does.
However the language rules do allow the compiler to remove the
call to the copy constructor and initialize x directly.
| Quote: |
This could apply to any type with a purely compiler generated
copy constructor, say both of:
copy constructor implicitly generated by compiler is always public -
|
Didn't say it wasn't.
But public access dosen't mean that the compiler has access to all
the source for the copy constructor (hence my use of the term pure
above).
| Quote: | again, I do not see any problem here. However, I still see problem that
I do not understand rationale behind clause cited in my post.
|
Rob.
--
http://www.victim-prime.dsl.pipex.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
johnchx Guest
|
Posted: Tue Oct 26, 2004 10:45 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
"Andrew Koenig" <ark (AT) acm (DOT) org> wrote
| Quote: | "Bronek Kozicki" <brok (AT) rubikon (DOT) pl> wrote i
What's purpose of this clause in the C++ standard? Under what scenarios
it's useful enough to justify cost?
The purpose is to reduce the chance that code that compiles on one compiler
will fail to compile on another. Such situations make testing difficult,
because if your only compiler accepts a program, there is no easy way of
determining that another compiler will reject it.
|
Well sure, I suppose that's the purpose of *all* clauses in the
standard. :-)
But isn't the real question *why* the copy ctor needs to be accessible
in order to bind a reference to an rvalue of class type? I.e. is
there some architecture or implementation approach that would make it
difficult or impossible to bind a reference to an rvalue of class type
without copying?
I can't think of such a scenario that also allows us to call a
non-static member function on an rvalue of class type without copying.
But maybe I'm just overlooking something...stranger things have
happened.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Tue Oct 26, 2004 11:30 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
"Andrew Koenig" <ark (AT) acm (DOT) org> wrote
| Quote: | "Bronek Kozicki" <brok (AT) rubikon (DOT) pl> wrote in message
news:872cd$417a14ef$3e79757c$5254 (AT) nf1 (DOT) news-service-com...
What's purpose of this clause in the C++ standard? Under what
scenarios it's useful enough to justify cost?
The purpose is to reduce the chance that code that compiles on one
compiler will fail to compile on another. Such situations make testing
difficult, because if your only compiler accepts a program, there is
no easy way of determining that another compiler will reject it.
|
I think that the real question is: given a temporary of type T, and a
reference T const&, why does the standard allow making a copy?
There is a small category of classes which are designed to be used
almost exclusively as temporaries -- my GB_Format (and presumably also
boost::format) would be an example. The way I've implemented GB_Format,
there is internal state which means that the semantics of copying are at
best dubious. A deep copy would be very expensive, and a shallow copy
would cause problems with the state. Historically, I simply forbade
copying. With standard conformant compilers, however, I can't because
the standard requires an accessible copy constructor. So I'm forced to
define some arbitrary copy semantics which aren't really useful, or even
usable.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Rob Williscroft Guest
|
Posted: Wed Oct 27, 2004 12:02 am Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
johnchx wrote in news:4fb4137d.0410250605.289bac30 (AT) posting (DOT) google.com in
comp.lang.c++.moderated:
| Quote: | Rob Williscroft <rtw (AT) freenet (DOT) co.uk> wrote
The thing about rvalue's is they aren't like other object's,
int f() { return 0; }
f() return's an rvalue and on most platform's this rvalue will
be in a register, so to bind this to a int const & a copy *must*
be made.
This could apply to any type with a purely compiler generated
copy constructor, say both of:
struct A { int a, b; };
struct B : A { int c; };
Or, for sufficiently smart compilers, any type with a purely
compiler generated or inline (or templated) copy constructors.
A rationale along these lines occured to me too, but I don't think it
actually holds water. Two problems:
(1) An rvalue of class type always denotes an object (3.10/2) and an
object is a region of storage (1.8/1).
Only an rvalue of built-in
type is allowed to denote a value which is not an object (i.e. which
exists only in a register).
|
I'm not finding any justification for the above is it a quote or
a conclusion ?
| Quote: | A compiler might be able to return a
value of class type in a register as an optimization under the as-if
rule, but only if it can do so without affecting the semantics of the
program.
(2) It is possible to call a non-static member function of a class via
an rvalue, without copying the object denoted by the rvalue.
|
Is it /possible/ to do so (i.e. without the copy) or is it *required* to
do so, and do you have a quote/citation ?
| Quote: | That
means that it is always possible to obtain the object's address
(supplied as the this pointer) without invoking the copy constructor
(or requiring that it be accessible).
|
Even so, when a class type has no member function's or all its member
function's are defined as inline this is not going to be a problem.
Further if a class defenition is sutible for puting in a register,
say:
struct X { int i; int f() };
Why cannot the compiler create rvalue version's of all members:
int f()
{
return i;
}
i.e:
extern "C" int __X_int_f_void_lvalue( /*register*/ X *that )
{
return that->i;
}
extern "C" int __X_int_f_void_rvalue( /*register*/ int member_i )
{
return member_i;
}
The linker can eliminate either (or both) if unused and the
compiler would return the first for &X::f.
Rob.
--
http://www.victim-prime.dsl.pipex.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Peter Dimov Guest
|
Posted: Wed Oct 27, 2004 12:03 am Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Bronek Kozicki <brok (AT) rubikon (DOT) pl> wrote
| Quote: | This question has been asked on comp.std.c++ week ago, no answer yet.
Here's relevant part of the C++ standard, clause 8.5.3/5:
---- citation begin
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):
[...]
- 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.
The constructor that would be used to make the copy shall be callable
whether or not the copy is actually done.
---- citation end
Effectively rvalue of non-copyable class cannot be bound to const
reference. [...]
What's purpose of this clause in the C++ standard?
|
I can construct a hypothetical example of an architecture that might
need to make a copy when binding a class rvalue to a const reference.
Imagine a CPU where the two registers R0 and R1 have addresses 0x10
and 0x14, and consider the following example:
class X
{
private:
int a;
int * p; // inv: p == &a
public:
X(): a( 0 ), p ( &a ) {}
X( X const & rhs ): a( rhs.a ), p ( &a ) {}
};
void f( X const & x );
int main()
{
f( X() );
}
X() creates an rvalue that lives in R0=0 and R1=0x10. A reference to
this object needs to be passed to f. However, passing 0x10, the
address of R0, would be bad, because f would likely need to use the
registers itself. Therefore, a copy of the rvalue needs to be made. In
order to preserve the invariant of X, this copy needs to be done via
the copy constructor.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
johnchx Guest
|
Posted: Thu Oct 28, 2004 1:52 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
[email]pdimov (AT) gmail (DOT) com[/email] (Peter Dimov) wrote
| Quote: | I can construct a hypothetical example of an architecture that might
need to make a copy when binding a class rvalue to a const reference.
Imagine a CPU where the two registers R0 and R1 have addresses 0x10
and 0x14, and consider the following example:
class X
{
private:
int a;
int * p; // inv: p == &a
public:
X(): a( 0 ), p ( &a ) {}
X( X const & rhs ): a( rhs.a ), p ( &a ) {}
};
void f( X const & x );
int main()
{
f( X() );
}
X() creates an rvalue that lives in R0=0 and R1=0x10. A reference to
this object needs to be passed to f. However, passing 0x10, the
address of R0, would be bad, because f would likely need to use the
registers itself. Therefore, a copy of the rvalue needs to be made. In
order to preserve the invariant of X, this copy needs to be done via
the copy constructor.
|
OK, but....
(a) How do you call a member function of a temporary X without
copying?
(b) Since f() could clobber R0 and/or R1, thus ending the lifetime of
the temporary (by re-using its "storage") the implementation would
have to ensure that ~X() (if it were non-trivial) would be executed
before entry to the function. That would violate the order of
destruction of temporaries created when binding an rvalue to a
reference.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Bronek Kozicki Guest
|
Posted: Thu Oct 28, 2004 1:59 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Peter Dimov wrote:
| Quote: | class X
{
private:
int a;
int * p; // inv: p == &a
public:
X(): a( 0 ), p ( &a ) {}
[...]
X() creates an rvalue that lives in R0=0 and R1=0x10. A reference to
[...] |
I would argue that any value of X type (rvalue or lvalue) may *not* live
in registers, because one of its members is initialized with address of
the other one. Thus, in order for X::p to be initialized with valid
memory address, object needs to be placed in memory - where it will stay
for the duration of function "void f(X const &)" call). Thus there's
still no need to make a copy, unless you need aggresive optimization -
and as we know, any kind of optimization is allowed by the C++ standard
under "as if" rule.
B.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Bronek Kozicki Guest
|
Posted: Thu Oct 28, 2004 2:01 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Andrew Koenig wrote:
| Quote: | The purpose is to reduce the chance that code that compiles on one
compiler will fail to compile on another. Such situations make
testing difficult, because if your only compiler accepts a program,
there is no easy way of determining that another compiler will reject
it.
|
Thank you for your answer. I'm really pleased to see authorities like
you interested in this issue. But still I believe that in this case
solution (ie. clause I cited) of the problem (ie. standarized behaviour)
that you described is just opposite to intended. That's because code
that intuitively should be correct - is actually ill-formed due to said
clause (see code snippets in my first post, also James Kanze post in
this thread describing more general problem). Thus currently the
situation is that some compilers choose to compile such (ill-formed)
code, fulfilling programmers expectations, while some compilers reject
such code as ill-formed. Even some best conforming compilers (like
Comeau) provide compilation options that allow compilation of such
(ill-formed) code, at least on specific platform. Also, many compilers
are not consistent in what they choose to allow and what they choose to
reject (ie. of two snippets presented in my first post only one might be
compiled, but the other rejected). Thus result of said clause is just
opposite to indented (ie. standarized behaviour) - code that can be
compiled on some compiler is rejected by another. Clearly bad case of
standarization, and clear indication that there is existing practice
that is just opposite to the C++ standard.
I was directed by Michiel Salters (on comp.std.c++ ) to CWG issues 391,
I also checked 291 and 450. So, it appears that C++ Committee is aware
of the issue and is working on solution. That's great; do you know if
there is any chance to change C++ standard to required direct binding of
rvalue, thus eliminating need for publicly accessible copy constructor?
If so, note in "Notes from the March 2004 meeting" probably contains
error (as Michiel Salters noted on comp.std.c++ ), where it should be
reported?
Thank you and kind regards
Bronek Kozicki
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Peter Dimov Guest
|
Posted: Fri Oct 29, 2004 2:34 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
[email]johnchx2 (AT) yahoo (DOT) com[/email] (johnchx) wrote in message
news:<4fb4137d.0410270611.5ff48e35 (AT) posting (DOT) google.com>...
| Quote: | pdimov (AT) gmail (DOT) com (Peter Dimov) wrote
[...]
X() creates an rvalue that lives in R0=0 and R1=0x10. A reference to
this object needs to be passed to f. However, passing 0x10, the
address of R0, would be bad, because f would likely need to use the
registers itself. Therefore, a copy of the rvalue needs to be made. In
order to preserve the invariant of X, this copy needs to be done via
the copy constructor.
OK, but....
(a) How do you call a member function of a temporary X without
copying?
|
Yes, I realized this too, right after posting. X().f() and f( X() )
are no different.
| Quote: | (b) Since f() could clobber R0 and/or R1, thus ending the lifetime of
the temporary (by re-using its "storage") the implementation would
have to ensure that ~X() (if it were non-trivial) would be executed
before entry to the function. That would violate the order of
destruction of temporaries created when binding an rvalue to a
reference.
|
It seems that the caller can just save and restore R0 and R1... but I
think that you're right for this case, too. X::X() can register the
object in a registry which f() can then enumerate.
Well, I'm out of ideas. :-)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
David Abrahams Guest
|
Posted: Fri Oct 29, 2004 2:41 pm Post subject: Re: unanswered question about clause 8.5.3/5 |
|
|
Bronek Kozicki <brok (AT) rubikon (DOT) pl> wrote
| Quote: | This question has been asked on comp.std.c++ week ago, no answer yet.
Here's relevant part of the C++ standard, clause 8.5.3/5:
---- citation begin
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):
[...]
- 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.
The constructor that would be used to make the copy shall be callable
whether or not the copy is actually done.
---- citation end
Effectively rvalue of non-copyable class cannot be bound to const
reference. Thus following code is ill-formed:
|
<snip code>
| Quote: | Some compilers seem to not implement this clause, ie. will compile at
least one of above code samples without error. These compilers are GCC
3.3.1 (regardless of compilation options), MSVC (all versions, with
"language extensions" enabled), Comeau (in non-strict mode), and
possibly more.
|
By one valid interpretation, it's Comeau in strict mode that's
non-conforming.
See http://tinyurl.com/5smro#the-interpretation-problem
Fortunately, the CWG resolved in Redmond that there is never any need
for a copy of the temporary, so the rule can be lifted.
HTH,
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|