Topic: Synthesized assignment and virtual bases
Author: jason@cygnus.com (Jason Merrill)
Date: Wed, 9 Feb 1994 00:08:28 GMT Raw View
12.8 says, "Objects representing virtual base classes will be assigned only
once by a generated assignment operator." This is trickier to support than
is the equivalent statement for constructors, since a base initializer for
a virtual base that is called by a class other than the most fully-derived
for that object is ignored.
Consider the following code. Should D have a synthesized assignment
operator? How many times should A::op= be called? I would say yes, and
one, by the above rule. xlC 1.2 and g++ 2.5.8 call it once, Cfront 3.0.1
calls it twice.
*************
int count = 0;
extern "C" int printf (const char *, ...);
class A {
public:
A& operator = (const A&) { count++; return *this; }
};
class B: virtual public A { };
class C: virtual public A { };
class D: public B, public C { };
int main()
{
D a, b;
a = b;
printf ("%d\n", count);
}
*************
Now suppose B defines op= to do nothing. Now how many times should A::op=
be called? I would say zero, because the compiler does not have control
over all paths to A, and so it should abdicate responsibility for
initializing the A part of D. xlC doesn't call it, g++ and Cfront call it
once.
Suppose, instead, that B and C both inherit from A via virtual private
inheritance? Should D still have a synthesized assignment operator? For
the compiler to make sure that A::op= is only called once, it needs to
prowl the inheritance DAG, but D can't refer to A::op=, so maybe it
shouldn't synthesize op=. xlC doesn't, even if both B and C define op=.
Cfront and g++ do.
Thoughts?
Jason
Author: daniels@biles.com (Brad Daniels)
Date: Wed, 9 Feb 1994 16:41:37 GMT Raw View
In article <JASON.94Feb8160828@deneb.cygnus.com>,
Jason Merrill <jason@cygnus.com> wrote:
>12.8 says, "Objects representing virtual base classes will be assigned only
>once by a generated assignment operator." This is trickier to support than
>is the equivalent statement for constructors, since a base initializer for
>a virtual base that is called by a class other than the most fully-derived
>for that object is ignored.
>
>Consider the following code. Should D have a synthesized assignment
>operator? How many times should A::op= be called? I would say yes, and
>one, by the above rule. xlC 1.2 and g++ 2.5.8 call it once, Cfront 3.0.1
>calls it twice.
Yes/one sounds right to me, too... The problem is the same as that of
synthesizing a copy ctor.
>*************
>int count = 0;
>extern "C" int printf (const char *, ...);
>
>class A {
> public:
> A& operator = (const A&) { count++; return *this; }
>};
>
>class B: virtual public A { };
>class C: virtual public A { };
>class D: public B, public C { };
>
>int main()
>{
> D a, b;
> a = b;
> printf ("%d\n", count);
>}
>*************
>
>Now suppose B defines op= to do nothing. Now how many times should A::op=
>be called? I would say zero, because the compiler does not have control
>over all paths to A, and so it should abdicate responsibility for
>initializing the A part of D. xlC doesn't call it, g++ and Cfront call it
>once.
I think it should either be called once, or the case should be flagged as
ambiguous. If B's op= doesn't invoke A's op=, while C's (being a default
op=) does, it's impossible for the compiler to determine whether it's more
important not to change A (to satisfy B's expectations) or to assign A
(to satisfy C's expectations). I personally favor throwing the case out
as ambiguous, since you can have similar problems even with both B and C
having defined op=;
class A {
public:
A& operator = (const A&) { count++; return *this; }
A& operator = (int i) { count+=i; return *this; }
};
class B: virtual public A {
public:
B& operator = (const B &ob) { A::operator=(ob); return *this; }
};
class C: virtual public A {
int i;
public:
C& operator = (const C &ob) { A::operator=(ob.i); return *this; }
};
class D: public B, public C { };
int main()
{
D a, b;
a = b;
printf ("%d\n", count);
}
In the above (obviously incomplete) example, any result might surprise
future invocations of either B or C. I think flagging an error is usually
the best way to handle such cases.
>Suppose, instead, that B and C both inherit from A via virtual private
>inheritance? Should D still have a synthesized assignment operator? For
>the compiler to make sure that A::op= is only called once, it needs to
>prowl the inheritance DAG, but D can't refer to A::op=, so maybe it
>shouldn't synthesize op=. xlC doesn't, even if both B and C define op=.
>Cfront and g++ do.