Topic: `X: :X(X&)' makes `X foo(); X x(foo())' illegal?
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 3 Jul 1994 15:21:58 GMT Raw View
>From: jason@cygnus.com (Jason Merrill)
>>> The question now is: "what behaviour of the copy constructor has been
>>> adopted by the standard language definition?". Or even worse: "What is a
>>> copy constructor, 'X::X(const X&)', 'X::X(X&)' or both of them, avoiding
>>> the overloading of const and non-const parameters?".
>
>> IMHO: Either of these signatures is a copy constructor. If either
>> is defined by the user, nothing will be generated. If neither is
>> defined, X::X(const X&) is generated if possible, if not then X::X(X&) is
>> generated if possible, if not no copy constructor is generated.
>
>And if both are defined? Is the code ill-formed?
No, why should it be? Then there are two copy constructors :-)
X::X(const volatile&)
X::X(volatile&)
are not copy constructors.
>
>How about copy assignment operators? Assuming that X::operator=(const X&)
>and X::operator=(X&) are copy assignment operators,
Seems reasonable.
>is X::operator=(X)?
Has to be I suppose.
>X::operator=(Xbase&)?
Nope. Thats not a "copy" assignment. IMHO.
However, the rules for assignment need to be different. The simplest
rules are:
To generate an assignment, call the (body of the) copy assignment of
each direct non-virtual base and each member. If a class has a virtual
base, refuse to generate an assignment.
A better rule can probably be found. Some people think
allowing virtual bases to be copied twice is OK. I dont like it
much. Assignment is a can of worms. How does the user write
an assignment? It seems necessary to be able to call generated
assignments by name:
Base1::operator=(x);
Base2::operator=(x);
or the equivalent using a cast:
*((Base1*)this) = x;
*((Base2*)this) = x;
If a class has a virtual base, one needs to factor the assignment
void assign_non_virtuals(Derived const&) { .. }
void assign_virtual1(vBase1 const&) { .. }
..
void operator=(Derived const& x)
{
assign_non_virtuals(x);
assign_virtual1(x);
..
}
so that even more derived classes only call the non-virtual
assignments of each base, then call the virtual assignments
for each virtual basse (once). (Gak :-(
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd, CSERVE:10236.1703
6 MacKay St ASHFIELD, Mem: SA IT/9/22,SC22/WG21
NSW 2131, AUSTRALIA
Author: jason@cygnus.com (Jason Merrill)
Date: Wed, 6 Jul 1994 04:04:52 GMT Raw View
>>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:
> X::X(const volatile&)
> X::X(volatile&)
> are not copy constructors.
Why not? I agree that the WP does not mention these as possibilities, but
was this an deliberate omission?
>> X::operator=(Xbase&)?
> Nope. Thats not a "copy" assignment. IMHO.
Looking over the WP text involving assignment, I notice the complete
absence of anything distinguishing a "copy" assignment from any others; as
far as I can tell, the WP states that *any* assignment operator will
suppress the synthesized version. None of the compilers I have access to
implement this rule exactly. For instance, in this example both Cfront and
xlC use operator= (int) to copy 'a', but commenting out operator int causes a
synthesized operator= to be used. Do other compilers work this way? It
seems dangerous.
extern "C" int printf (const char *, ...);
struct A {
void operator = (int) { printf ("op=(int)\n"); }
operator int () { return 42; }
};
main()
{
A a;
a = a;
}
Jason
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 27 Jun 1994 21:21:22 GMT Raw View
In article <1994Jun17.093411.8447@tid.es> pascual@peggy.tid.es (Pascual Juan) writes:
>
>The question now is: "what behaviour of the copy constructor has been adopted
>by the standard language definition?". Or even worse: "What is a copy
>constructor, 'X::X(const X&)', 'X::X(X&)' or both of them, avoiding the
>overloading of const and non-const parameters?".
IMHO: Either of these signatures is a copy constructor.
If either is defined by the user, nothing will be generated.
If neither is defined, X::X(const X&) is generated if possible,
if not then X::X(X&) is generated if possible, if not no
copy constructor is generated.
For the constructor X::X(X&) the argument must
be an lvalue and the constructor is permitted to modify it.
This is important for objects representing resources.
This raises doubts about the notion that
"copy constructors can be inserted or elided at the whim
of the compiler".
I would have thought the importance of having
a constructor of the form:
X::X(X&)
was to prevent copying temporaries -- and thus prevent the
compiler inserting such copy constructors at whim.
In fact, one could use the constructor as a _move_ method:
X::X(X& x) : mem(x.mem) { x.~X(); }
which destroys the source. (Not that I recommend this)
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd, CSERVE:10236.1703
6 MacKay St ASHFIELD, Mem: SA IT/9/22,SC22/WG21
NSW 2131, AUSTRALIA
Author: jason@cygnus.com (Jason Merrill)
Date: Tue, 28 Jun 1994 04:17:52 GMT Raw View
>>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:
> In article <1994Jun17.093411.8447@tid.es> pascual@peggy.tid.es (Pascual
Juan) writes:
>> The question now is: "what behaviour of the copy constructor has been
>> adopted by the standard language definition?". Or even worse: "What is a
>> copy constructor, 'X::X(const X&)', 'X::X(X&)' or both of them, avoiding
>> the overloading of const and non-const parameters?".
> IMHO: Either of these signatures is a copy constructor. If either
> is defined by the user, nothing will be generated. If neither is
> defined, X::X(const X&) is generated if possible, if not then X::X(X&) is
> generated if possible, if not no copy constructor is generated.
And if both are defined? Is the code ill-formed?
How about copy assignment operators? Assuming that X::operator=(const X&)
and X::operator=(X&) are copy assignment operators, is X::operator=(X)?
X::operator=(Xbase&)?
Jason
Author: pascual@peggy.tid.es (Pascual Juan)
Date: Tue, 28 Jun 1994 12:19:35 GMT Raw View
In article <JASON.94Jun27211752@deneb.cygnus.com>, jason@cygnus.com (Jason Merrill) writes:
|> >>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:
|>
|> > In article <1994Jun17.093411.8447@tid.es> pascual@peggy.tid.es (Pascual
|> Juan) writes:
|>
|> >> The question now is: "what behaviour of the copy constructor has been
|> >> adopted by the standard language definition?". Or even worse: "What is a
|> >> copy constructor, 'X::X(const X&)', 'X::X(X&)' or both of them, avoiding
|> >> the overloading of const and non-const parameters?".
|>
|> > IMHO: Either of these signatures is a copy constructor. If either
|> > is defined by the user, nothing will be generated. If neither is
|> > defined, X::X(const X&) is generated if possible, if not then X::X(X&) is
|> > generated if possible, if not no copy constructor is generated.
|>
|> And if both are defined? Is the code ill-formed?
In the meanwhile between I posted the article and I received the followups
I became an observer member of the committee. In chapter 12.1 box 56 of the
draft it can be seen the need for a non-trivial definition of implicity
declared copy constructors. The definition made by Max Skaller is good
enought, but changing "generated" to "declared", because (acording to the
draft) definition for an implicity-declared copy constructor will be generated
only if it is called.
|> How about copy assignment operators? Assuming that X::operator=(const X&)
|> and X::operator=(X&) are copy assignment operators, is X::operator=(X)?
Acording to draft 12.8.8:
"If a class X has any X::operator=() that has a parameter of class X, the
dafault assignment will not be generated".
So any of them will make dafault assignment not to be generated.
|> X::operator=(Xbase&)?
|> Jason
In this case Xbase is not X so I guess it won't avoid default assignment
to be generated.
--
-------------------------------------------------------------------------------
|||| ## #### | Pascual Juan | _V_
|||| ## _|_ ## ## Telefonica I+D | E-mail: pascual@gonzo.tid.es | _(,|,)`
@@@@ ## | ## ## (Telefonica R&D) | Phone: +34-1-337-47-04 | | ___ ')
@@@@ ## #### | fax: +34-1-337-42-22 | |_|`__/
-------------------------------------------------------------------------------
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 29 Jun 1994 02:33:53 GMT Raw View
In article <1994Jun28.121935.3812@tid.es> pascual@peggy.tid.es (Pascual Juan) writes:
>|> >> copy constructor, 'X::X(const X&)', 'X::X(X&)' or both of them, avoiding
>|> >> the overloading of const and non-const parameters?".
>|>
>|> > IMHO: Either of these signatures is a copy constructor. If either
>|> > is defined by the user, nothing will be generated. If neither is
>|> > defined, X::X(const X&) is generated if possible, if not then X::X(X&) is
>|> > generated if possible, if not no copy constructor is generated.
>|>
>|> And if both are defined? Is the code ill-formed?
>
>The definition made by Max Skaller is good
>enought, but changing "generated" to "declared", because (acording to the
>draft) definition for an implicity-declared copy constructor will be generated
>only if it is called.
Woops, you are right. I should NOT have said "is generated
if possible" but "is generated if possible if a copy constructor
is called".
>
>|> How about copy assignment operators? Assuming that X::operator=(const X&)
>|> and X::operator=(X&) are copy assignment operators, is X::operator=(X)?
>
>Acording to draft 12.8.8:
>"If a class X has any X::operator=() that has a parameter of class X, the
>dafault assignment will not be generated".
>So any of them will make dafault assignment not to be generated.
That of course is not certain, unless
"has a parameter of class X" is properly defined.
>
>|> X::operator=(Xbase&)?
>|> Jason
>
>In this case Xbase is not X so I guess it won't avoid default assignment
>to be generated.
True IMHO. BUT if XBase DOES have a user defined
assignment operator of type
Xbase:: operator(Xbase cv &)
what happens? That is, is it called by the X generated copy
assignment operator to copy the base subobject?
What if the base is virtual?
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd, CSERVE:10236.1703
6 MacKay St ASHFIELD, Mem: SA IT/9/22,SC22/WG21
NSW 2131, AUSTRALIA
Author: pascual@peggy.tid.es (Pascual Juan)
Date: Wed, 29 Jun 1994 09:57:00 GMT Raw View
In article <Cs50GH.2pv@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller) writes:
|> >|> How about copy assignment operators? Assuming that X::operator=(const X&)
|> >|> and X::operator=(X&) are copy assignment operators, is X::operator=(X)?
|> >
|> >Acording to draft 12.8.8:
|> >"If a class X has any X::operator=() that has a parameter of class X, the
|> >dafault assignment will not be generated".
|> >So any of them will make dafault assignment not to be generated.
|>
|> That of course is not certain, unless
|> "has a parameter of class X" is properly defined.
WP needs a lot of trivial definitions like "has a parameter of class X".
Maybe... "A function has a parameter of class X if any of its parameters is
of type X or derived class, and it is passed by copy, reference or
const reference". But I have to apologize. If there exist both operators,
user defined X::operator=(Xbase&) and default defined X::operator=(const X&)
all the X's are Xbase's so there is an ambiguity. an_x=other_x can use
both operators. I made an example program and C-front don't generate
default assignment if it realizes it can introduce the ambiguity.
-------------------------------------------------------------------------------
#include <iostream.h>
class B
{
public:
B& operator=(B&) { cout << "B::operator=(B&);" << endl; return *this; }
};
class D: public B
{
public:
int data;
D(int parameter):data(parameter){};
D& operator=(B&) { cout << "D::operator=(B&);" << endl; return *this; }
};
void main()
{
D d1(1), d2(2);
d2=d1;
}
-------------------------------------------------------------------------------
The output of this program is:
D::operator=(B&);
So D::operator=(const D&) is not generated (at least in C-front).
IMHO draft 12.8.8 has to be modified to:
"If a class X has any X::operator=() that can be called with an instance
of class X as parameter, the dafault assignment will not be generated".
|> >|> X::operator=(Xbase&)?
|> >|> Jason
|> >
|> >In this case Xbase is not X so I guess it won't avoid default assignment
|> >to be generated.
|>
|> True IMHO. BUT if XBase DOES have a user defined
|> assignment operator of type
|>
|> Xbase:: operator(Xbase cv &)
|>
|> what happens? That is, is it called by the X generated copy
|> assignment operator to copy the base subobject?
|>
|> What if the base is virtual?
I can't find in the WP the specification of the definition of the default
assignment operator (just its interface in 12.8 6,7,8), but the C-front
does the following:
-------------------------------------------------------------------------------
#include <iostream.h>
class B
{
public:
B& operator=(B&) { cout << "B::operator=(B&);" << endl; return *this; }
};
class D: public B
{
public:
int data;
D(int parameter):data(parameter){};
// There is no operator= so there will be a default one.
// D& operator=(B&) { cout << "D::operator=(B&);" << endl; return *this; }
};
void main()
{
D d1(1), d2(2);
d2=d1;
cout << d2.data << endl;
}
-------------------------------------------------------------------------------
The output of this program is:
B::operator=(B&);
1
So default assignment calls operator= declared in the base class and does
its work in the derived class (at least in the C-front).
I guess the behaviour of virtual operators will follow the same rules as
the virtual normal functions.
Excuse me for so long answer.
-------------------------------------------------------------------------------
|||| ## #### | Pascual Juan | _V_
|||| ## _|_ ## ## Telefonica I+D | E-mail: pascual@gonzo.tid.es | _(,|,)`
@@@@ ## | ## ## (Telefonica R&D) | Phone: +34-1-337-47-04 | | ___ ')
@@@@ ## #### | fax: +34-1-337-42-22 | |_|`__/
-------------------------------------------------------------------------------
Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 15 Jun 1994 17:41:26 GMT Raw View
Is the following program strictly conforming to the ARM and/or latest
working paper?
struct X {
X(X&) {}
};
X foo() { X x; return x; }
int main() {
X x(foo());
}
The problem is that it initializes a non-const reference (the parameter
for the copy-constructor) with a temporary. Can anyone confirm that this
is indeed an error that must be diagnosed by conforming compilers?
What about using `X x = foo()' instead of `X x(foo())'?
If the are indeed illegal, as I suspect, does anyone know of any
compilers which do diagnose either of these errors?
None of the compilers I tried reported an error, even though one of
them issued a diagnostic for the very similar situation
struct X {
X(int, X&) {}
};
X foo() { X x; return x; }
int main() {
X x(0, foo())
}
which differs only in the extra `int' argument for the constructor.
--
Fergus Henderson - fjh@munta.cs.mu.oz.au
Author: mendell@opus.torolab.ibm.com (Mark Mendell)
Date: 15 Jun 1994 20:08:28 GMT Raw View
C Set ++ for AIX 2.1 gives the following errors:
"t.C", line 4.21: 1540-195: (S) No default constructor exists for "X".
"t.C", line 7.24: 1540-224: (S) Conversion from "X" to a reference to a non-const type "X&" requires a temporary.
"t.C", line 7.24: 1540-306: (I) The previous message applies to argument 1 of function "X::X(X&)".
"t.C", line 8.9: 1540-331: (E) Return value of type "int" is expected.
--
Mark Mendell
C Set ++ for AIX Development
IBM Toronto Lab
mendell@vnet.ibm.com
Author: robert@idt.unit.no (Robert Schmidt)
Date: 16 Jun 1994 12:23:38 GMT Raw View
In article <9416703.4963@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
|> Is the following program strictly conforming to the ARM and/or latest
|> working paper?
|>
|> struct X {
|> X(X&) {}
|> };
|> X foo() { X x; return x; }
^^^^
Borland C++ will complain about a missing default constructor here (error).
I tried it with G++ on this Sun box:
pil:~/c% gcc t.cc
t.cc: In function `struct X foo ()':
t.cc:8: too few arguments for constructor `X'
t.cc:8: in base initialization for class `X'
However, I suspect that you simply forgot to type it in, so I included
a default constructor, and G++ compiled it fine. Read on for comments.
|> int main() {
|> X x(foo());
|> }
|>
|> The problem is that it initializes a non-const reference (the parameter
|> for the copy-constructor) with a temporary. Can anyone confirm that this
|> is indeed an error that must be diagnosed by conforming compilers?
|> What about using `X x = foo()' instead of `X x(foo())'?
No, compilers need not report this as an error. The lifetime of a
temporary has been the cause of much discussion - I think the conclusion
was that temporaries are *guaranteed* to live until the end of the full
expression, i.e. an expression which is not a sub-expression. There
aren't many (if any) compilers conforming to this as of yet, though.
So, the temporary returned from foo() should at least live until the end of
"X x(foo())", which is a full expression according to the latest standard
(proposal?) - i.e. the declaration has a value (according to Stroustroup:
"The design and evolution of C++" - great book). I think the ARM defines
the lifetime to be to the end of a {} block.
The problem is the same as the one Stroustroup uses as a frequent example
in his books, in which a class String has defined a friend operator +, which
returns a temporary String. No matter what strategy he selects for
determining the temporary lifetime, he has an example which breaks it.
The only fool-proof alternative would be garbage collection, which is pretty
much out anyway (I, for one, do not want it).
|> If the are indeed illegal, as I suspect, does anyone know of any
|> compilers which do diagnose either of these errors?
Wether or not they are errors depend on what version of the standard
a compiler is based on, and on what you do with the reference in the
constructor. If you save a pointer to it or any of its members (shallow
copy), you *might* be in trouble (dangling pointers if the temporary is thrown
away after the initialization). If you do a _deep_ copy, you're fine.
This is a difficult topic - one should avoid such constructs when
in doubt of the temporary lifetimes. Use proper temporary variables
instead.
|> None of the compilers I tried reported an error, even though one of
|> them issued a diagnostic for the very similar situation
|>
|> struct X {
|> X(int, X&) {}
|> };
|> X foo() { X x; return x; }
|>
|> int main() {
|> X x(0, foo())
|> }
|>
|> which differs only in the extra `int' argument for the constructor.
What did the diagnostic say? I can't see any reason for reporting
anything here if it didn't happen above - except for the missing
default constructor (which every compiler should flag as an error).
--
Robert Schmidt - robert@stud.unit.no
If all you have is a hammer, everything looks like a nail.
Author: mjn@netcom.com (The Sixth Replicant)
Date: Thu, 16 Jun 1994 21:45:44 GMT Raw View
In article <9416703.4963@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>Is the following program strictly conforming to the ARM and/or latest
>working paper?
>
> struct X {
> X(X&) {}
> };
> X foo() { X x; return x; }
>
> int main() {
> X x(foo());
> }
>
>The problem is that it initializes a non-const reference (the parameter
>for the copy-constructor) with a temporary. Can anyone confirm that this
>is indeed an error that must be diagnosed by conforming compilers?
>What about using `X x = foo()' instead of `X x(foo())'?
>
>If the are indeed illegal, as I suspect, does anyone know of any
>compilers which do diagnose either of these errors?
>None of the compilers I tried reported an error, even though one of
>them issued a diagnostic for the very similar situation
>...
IBM's CSet++ compiler v2.01 for OS/2 reported:
12 | X x(foo());
y.cpp(12:1) : error EDC3224: Conversion from "X" to a reference to a non-const
type "X&" requires a temporary.
y.cpp(12:1) : informational EDC3306: The previous message applies to argument 1
of function "X::X(X&)".
---------------------------------------------------------------------------
Author: jason@cygnus.com (Jason Merrill)
Date: Thu, 16 Jun 1994 22:08:20 GMT Raw View
>>>>> Robert Schmidt <robert@idt.unit.no> writes:
> No, compilers need not report this as an error. The lifetime of a
> temporary has been the cause of much discussion - I think the conclusion
> was that temporaries are *guaranteed* to live until the end of the full
> expression, i.e. an expression which is not a sub-expression. There
> aren't many (if any) compilers conforming to this as of yet, though.
Lifetime of temporaries isn't the issue here; the question is whether or
not you can bind a non-const reference to a temporary, and the answer is
currently no, since the temporary is not an lvalue.
Jason
Author: pascual@peggy.tid.es (Pascual Juan)
Date: Fri, 17 Jun 1994 09:34:11 GMT Raw View
In article <JASON.94Jun16150820@deneb.cygnus.com>, jason@cygnus.com (Jason Merrill) writes:
|> >>>>> Robert Schmidt <robert@idt.unit.no> writes:
|>
|> > No, compilers need not report this as an error. The lifetime of a
|> > temporary has been the cause of much discussion - I think the conclusion
|> > was that temporaries are *guaranteed* to live until the end of the full
|> > expression, i.e. an expression which is not a sub-expression. There
|> > aren't many (if any) compilers conforming to this as of yet, though.
|>
|> Lifetime of temporaries isn't the issue here; the question is whether or
|> not you can bind a non-const reference to a temporary, and the answer is
|> currently no, since the temporary is not an lvalue.
|>
|> Jason
I agree with Jason. Fergus has discoverred a hole in the compilers based in
AT&T's C-Front. I've been testing a variation of the code to enforce the
visualization of the mistake. It failed in HP's and SUN's version of the
C-Front 3.* (in HPUX-8 HPUX-9 SUN-OS and SOLARIS). Code is the following:
class X // Indentation has been done so to see the similarities.
{
public:
X() {}
X(X& x) {x.i=7;} // Both functions modifies the
void bar(X& x) {x.i=7;} // reference parameter "x".
int i;
};
X foo() { X x; return x; }
void main()
{
X x(foo()); // Compiler doesn't warn about temporary reference.
x.bar(foo()); // The same mistake is do warned in this case.
}
Compiler just says:
CC: "file.C", line 15: warning: temporary used for non-const X& argument; no changes will be propagated to actual argument (anachronism) (283)
avoiding to warn about line 14, which is doing the very same mistake.
I guess C-Front implementation forgets to delete the default copy contructor
declaration "X::X(const X&)" hoping the one defined by the programmer will
follow the default declaration. If so, all the calls to copy constructor are
allowed because the compiler works as "the great pretender".
The question now is: "what behaviour of the copy constructor has been adopted
by the standard language definition?". Or even worse: "What is a copy
constructor, 'X::X(const X&)', 'X::X(X&)' or both of them, avoiding the
overloading of const and non-const parameters?".
--
-------------------------------------------------------------------------------
|||| ## #### | Pascual Juan | _V_
|||| ## _|_ ## ## Telefonica I+D | E-mail: pascual@gonzo.tid.es | _(,|,)`
@@@@ ## | ## ## (Telefonica R&D) | Phone: +34-1-337-47-04 | | ___ ')
@@@@ ## #### | fax: +34-1-337-42-22 | |_|`__/
-------------------------------------------------------------------------------
Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 17 Jun 1994 19:28:04 GMT Raw View
In article <JASON.94Jun16150820@deneb.cygnus.com> jason@cygnus.com (Jason Merrill) writes:
>>>>>> Robert Schmidt <robert@idt.unit.no> writes:
>
>Lifetime of temporaries isn't the issue here; the question is whether or
>not you can bind a non-const reference to a temporary, and the answer is
>currently no, since the temporary is not an lvalue.
If the temporary is of class type T and the class
contains a member function:
T& T::cheat() { return *this; }
the answer is yes, you can do it, but not directly. How is this
possible? Well, because a member function CAN bind
a temporary with a non-const reference, namely *this.
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd, CSERVE:10236.1703
6 MacKay St ASHFIELD, Mem: SA IT/9/22,SC22/WG21
NSW 2131, AUSTRALIA