I don't know; but it could be true. [3.8/7] comes to mind.
In practice, of course, you will not see a problem; and one
can argue that [3.8/7] is overspecified. The last item in the
list seems to be written with this->~T() in mind. (The example
that follows this provision in the current draft n2461 seems
to support that interpretation.)
I'm not sure that the problem is 3.8/7; I think that that
concerns complete objects. The problem here is that you are
destructing a subobject, without notifying the object it
contains. In practice, it would probably work with members, but
I don't see how a compiler could make it work for base classes;
the usual object model involves base and derived classes sharing
some (or all) of the vptr, for example.
In practice, it doesn't work with g++, Sun CC or VC++. Try the
following:
class B
{
public:
virtual ~B() {}
virtual void f() ;
B& operator=( B const& other ) ;
} ;
class D : public B
{
public:
virtual void f() ;
} ;
B&
B:
perator=(
B const& other )
{
if ( this != &other ) {
this->B::~B() ;
new (this ) B( other ) ;
}
return *this ;
}
void
B::f()
{
std::cout << "B::f()" << std::endl ;
}
void
D::f()
{
std::cout << "D::f()" << std::endl ;
}
int main()
{
D aD ;
D* pD = &aD ;
std::cout << typeid( aD ).name() << std::endl ;
std::cout << "from obj.: " ;
aD.f() ;
std::cout << "from ptr.: " ;
pD->f() ;
aD = D() ;
std::cout << typeid( aD ).name() << std::endl ;
std::cout << "from obj.: " ;
aD.f() ;
std::cout << "from ptr.: " ;
pD->f() ;
return 0 ;
}
Not in particular the output of the last two lines, and that
you're calling f() on the *same* object in them. (If the code
were correct, of course, you'd get D::f() everywhere.)
Huh? I am not following. The derived object is still there and
can be used as an object of the derived type.
No it can't. That's the problem. The derived object and its
base sub-objects are intimely linked; in all of the
implementations I know, for example, they share a vptr (and this
is definitly allowed by the standard).
The situation would be even worse if virtual inheritance were
involved, because the position of the virtual base class in the
complete object depends on the actual type---and is usually
determined by more or less the same mechanism as virtual
functions.
I don't see how its type (static or dynamic) could have
changed in the process.
Because the dynamic type of an object is determined by the last
constructor called on the memory which contains the object.
Calling "new (this) Base" means that the object at that address
now has the dynamic type Base.
Here you argue as if the destructor call had torn down the
ambient derived object, whereas above you worried about
destructing the subobject while not destroying the ambient
object.
The destructor to the base will destroy part of the derived
class, conceptually, and in many cases really. When you execute
the destructor, the dynamic type of the object "isA" base. The
compiler will normally modify any RTTI (vptr, etc.) to reflect
this; if it doesn't in simple cases, it is only because the
compiler is able to determine that the vptr would not actually
be used after the change.
Similarly, the constructor constructs an object of complete type
B. After construction, the memory no longer contains a D; it
contains a B. §3.8/4 is certainly relevant here: by calling the
constructor of the base type, you have reused the memory which
previously contained a D. And we can see the motivation for
§3.8/7 in the above code; the compiler knows that the object at
the address &aD has type D, so whenever is dealing with an
object that it knows to be at this address (the object itself,
but not the contents of the pointer), it can treat it as having
type D.
What other benefits do you expect from an assignment operator?
I'm talking about the swap idiom: if I use a particular idiom,
it's because I expect some benefit from doing so. If all of the
sub-objects have a no-throw, relativly optimized swap, that's no
problem with the swap idiom because the benefits greatly
outweigh the cost. But if some subclass doesn't implement it,
then I'm not really implementing the swap idiom; I'm
implementing something that looks like it, but that isn't. I'm
confusing the reader (who recognizes the idiom, and assumes
something that isn't true), and I'm likely paying an unnecessary
performance penalty.