In an answer to Is it safe to store objects of a class which has an std::auto_ptr as its member variable in std::vector? I stated that a class that contained an auto_ptr could
Trying to put the list of places together that makes the example undefined behavior.
#include <memory>
#include <vector>
using namespace std;
struct Z {};
struct A {
A( Z z )
: p( new Z(z) ) {}
A( const A & a )
: p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}
// no assigment op or dtor defined by intent
auto_ptr <Z> p;
};
int main() {
vector <A> av;
...
}
I will examine the lines up to the one where you instantiate the vector with your type A
. The Standard has to say
In 23.1/3
:
The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignable types.
In 23.1/4
(emphasis mine):
In Table 64, T is the type used to instantiate the container, t is a value of T, and u is a value of (possibly const) T.
+-----------+---------------+---------------------+ |expression |return type |postcondition | +-----------+---------------+---------------------+ |t = u |T& |t is equivalent to u | +-----------+---------------+---------------------+
Table 64
In 12.8/10
:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. The implicitly-declared copy assignment operator for a class X will have the form
X& X::operator=(const X&)
if
- each direct base class B of X has a copy assignment operator whose parameter is of type const B&, const volatile B& or B, and
- for all the nonstatic data members of X that are of a class type M (or array thereof), each such class type has a copy assignment operator whose parameter is of type const M&, const volatile M& or M.
Otherwise, the implicitly declared copy assignment operator will have the form
X& X::operator=(X&)
(Note the last and second last sentence)
In 17.4.3.6/1 and /2
:
In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ Standard Library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
In particular, the effects are undefined in the following cases:
- for types used as template arguments when instantiating a template component, if the operations on the type do not implement the semantics of the applicable Requirements subclause (20.1.5, 23.1, 24.1, 26.1). Operations on such types can report a failure by throwing an exception unless otherwise specified.
Now, if you look at the specification of auto_ptr
you will note it has a copy-assignment operator that takes a non-const auto_ptr
. Thus, the implicitly declared copy assignment operator of your class will also take a non-const type as its parameter. If you read the above places carefully, you will see how it says that instantiating a vector with your type as written is undefined behavior.
What about the following?
cout << av[ 0 ] << endl;
Also, conceptually, a copy should leave the item copied from unchanged. This is being violated in your implementation.
(It is quite another thing that your original code compiles fine with g++ -pedantic ...
and Comeau but not VS2005.)
I don't think it's necessarily the case that the above code will even compile. Surely the implementor of std::vector
is at liberty to require an assignment operator to be available, from const A&
?
And having just tried it, it doesn't compile on Visual Studio C++ 2008 Service Pack 1:
binary '=' : no operator found which takes a right-hand operand of type 'const A' (or there is no acceptable conversion)
My guess is that, on the guidance of Herb Sutter, the container classes in VC++ make every effort to impose the standard requirements on their type parameters, specifically to make it hard to use auto_ptr
with them. They may have overstepped the boundaries set by the standard of course, but I seem to remember it mandating true assignment as well as true copy construction.
It does compile in g++ 3.4.5, however.
Since the regular auto_ptr
semantic could suggests that the ownership is passed during the copying, I would rather use here boost::scoped_ptr
. Of course the assignment operator is missing.
Objects stored in containers are required to be "CopyConstructable" as well as "Assignable" (C++2008 23.1/3).
Your class tries to deal with the CopyConstructable requirement (though I'd argue it still doesn't meet it - I edited that argument out since it's not required and because it's arguable I suppose), but it doesn't deal with the Assignable requirement. To be Assignable (C++2008 23.1/4), the following must be true where t
is a value of T
and u
is a value of (possibly const
) T
:
t = u
returns aT&
andt
is equivalent tou
The standard also says in a note (20.4.5/3): "auto_ptr
does not meet the CopyConstructible and Assignable requirements for Standard Library container elements and thus instantiating a Standard Library container with an auto_ptr
results in undefined behavior."
Since you don't declare or define an assignment operator, an implicit one will be provided that uses the auto_ptr
's assignment operator, which definitely makes t
not equivalent to u
, not to mention that it won't work at all for "const T u
" values (which is what Earwicker's answer points out - I'm just pointing out the exact portion(s) of the standard).