Avoid automatic copy constructor generation

M

Marcel Müller

Hi,

is there a way to avoid the automatic copy constructor generation. I do
not want the object to be non-copyable. I simply do not want that
copying is done by the default copy constructor. But there is a
constructor that accepts the base class. This one should be used for
copying.

In fact i have a set of classes with a common abstract base. The
implementations do not have own data members. They only implement
different functionality. But is makes sense to do assignments and copy
construction between these set of types. This effectively changes the
functionality depending on the current type.


class ContainerBase
{ // ...
};

class Container1
: public ContainerBase
{ // no own data members!
public:
Container1()
{}
Container1(const ContainerBase& r)
: ContainerBase(r)
{}
Container1& operator=(const ContainerBase& r)
{ ContainerBase::eek:perator=(r); return *this; }
};

class Container2
: public ContainerBase
{ // no own data members!
public:
Container2()
{}
Container2(const ContainerBase& r)
: ContainerBase(r)
{}
Container2& operator=(const ContainerBase& r)
{ ContainerBase::eek:perator=(r); return *this; }
};


int main()
{ Container1 c1;
Container2 c2 = c1; // OK
Container1 c3 = c1; // Wrong! Invokes Container1(const Contarine1& r)
return 0;
}


Of course, I could implement the copy constructors to do the same thing
as the constructor from const ContainerBase&. But this is really
unneeded redundancy. And the copy constructor is much more complicated
in the real application than in this small example. Furthermore the auto
generated copy constructor is unusable. It has undefined behavior
because of some special internal storage dependencies. It also has to do
with multiple inheritance, so I could not move the code to a member
function either.

Is there a way to avoid the required redefinition of the copy
constructor for each class?


Marcel
 
J

Juha Nieminen

Marcel said:
is there a way to avoid the automatic copy constructor generation.

Yes: Implement your own copy constructor. That will make the compiler
to not to create a default one.
 
K

Kai-Uwe Bux

Marcel said:
Hi,

is there a way to avoid the automatic copy constructor generation. I do
not want the object to be non-copyable. I simply do not want that
copying is done by the default copy constructor. But there is a
constructor that accepts the base class. This one should be used for
copying.

In fact i have a set of classes with a common abstract base. The
implementations do not have own data members. They only implement
different functionality. But is makes sense to do assignments and copy
construction between these set of types. This effectively changes the
functionality depending on the current type.
[snip]

What about implementing a copy-constructor for the Base and then have

class Derived_1 : public Base {

Derived_1 ( Base const & other )
: Base ( other )
{}

Derived_1 ( Derived_1 const & other )
: Base ( other )
{}

};

and similarly for Derived_2, ... ?

I think, if the copy-constructor for Base is implemented properly, the
compiler-generated copy-constructor for the derived classes would do the
same thing as the above, so you could even ditch that. Now, _that_ looks
very much like the code, I snipped. I clearly do not understand your
problem. Maybe, you need to post more details, because I lack the
imagination for:
Furthermore the auto
generated copy constructor is unusable. It has undefined behavior
because of some special internal storage dependencies. It also has to do
with multiple inheritance, so I could not move the code to a member
function either.

If that is true, I do not see how the constructors _from_ the base can
possibly work correctly if they are just:
Container1(const ContainerBase& r)
: ContainerBase(r)
{}



Best

Kai-Uwe Bux
 
M

Marcel Müller

Hi,

Pete said:
And a note for the future: with C++0X you don't need to declare it private.

class C
{
public:
C(const C&) = delete;
};

thanks! This is the solution.

Unfortunately all my compilers are far away from C++0X, so I have to do
it the hard way so far.


Marcel
 
O

osmium

Pete Becker said:
And a note for the future: with C++0X you don't need to declare it
private.

class C
{
public:
C(const C&) = delete;
};

The "= delete" says that it doesn't exist. There's also "= default", to
tell the compiler to generate the default version even if it wouldn't
otherwise. These also apply to default constructors, generated assignment
operators, etc.

I might like that. That's the first exposure I have had to this new
language of which you speak. I think if it is going to take off, it needs a
better name. I thought, and still do, that C++ is a bad name. The God
awful moniker of this thing is even worse than C++ is.

It kind of reminds me of the Microsoft's annoying obsession to use
meaningless words such as "Explorer". I think the idea was that "Explorer"
is a better "Navigator". Maybe people would think that "Gamma" was a better
"C"?
 
I

Ian Collins

Pete said:
And a note for the future: with C++0X you don't need to declare it private.

class C
{
public:
C(const C&) = delete;
};

The "= delete" says that it doesn't exist. There's also "= default", to
tell the compiler to generate the default version even if it wouldn't
otherwise. These also apply to default constructors, generated
assignment operators, etc.
I can see the benefit of "= default", but what benefits does "= delete"
offer over private constructors?
 
J

Juha Nieminen

Pete said:
There's also "= default", to
tell the compiler to generate the default version even if it wouldn't
otherwise.

Will it be possible to call the compiler-generated copy constructor
from a user-defined copy constructor?
 
J

Juha Nieminen

Ian said:
I can see the benefit of "= default", but what benefits does "= delete"
offer over private constructors?

Declaring a constructor private is more a hack than a clean idiom. If
you accidentally call the copy constructor from the class itself, you
will not get a clean compiler error, but an obscure linker error, which
is not really the best possible solution.

(Moreover, if you are creating a precompiled library you will not get
any error at all. Only when this library is linked against an executable
the linker error will show up. This is ugly.)

With the "= delete" syntax you will get a clear compiler error at
compile time even if you accidentally call the copy constructor from
inside the class itself.
 
I

Ian Collins

Juha said:
Declaring a constructor private is more a hack than a clean idiom. If
you accidentally call the copy constructor from the class itself, you
will not get a clean compiler error, but an obscure linker error, which
is not really the best possible solution.
Which is why a non-copyable base class is often used.
 
J

Juha Nieminen

Victor said:
Aren't those exclusive?

I know that in the next C++ standard it will be possible to call one
constructor from another. Thus I was wondering if it would be possible
to call the compiler-generated copy constructor from a user-defined one.

It would be really nice if this would be possible, as it would make it
much easier to write copy constructors.

One problem with copy constructors (if you have implemented one
explicitly) is that each time you add a new member variable to your
class, you have to remember to add it to the copy constructor (and
assignment operator). This is extremely easy to forget. If the member
variable is just eg. an int, then it the default copying would be enough
for it, so calling the compiler-generated copy constructor would
automatically copy it without having to do anything special about it.
(The user-written copy constructor could then simply concentrate on the
members which do need special copying.)
 
E

Erik Wikström

That's true if the private constructors are declared directly in the
non-copyable class. I wonder whether =delete offers anything over
EBO/inheritance-based utilities like boost::noncopyable.

Elegance and simplicity. A private copy-constructor is a hack but
boost::noncopyable is much worse, a complex solution to a simple
problem. Why should I have to inherit from some other class just to make
my class non-copyable?
 
I

Ian Collins

Erik said:
Elegance and simplicity. A private copy-constructor is a hack but
boost::noncopyable is much worse, a complex solution to a simple
problem. Why should I have to inherit from some other class just to make
my class non-copyable?
It may be a hack, but it's an idiomatic one that's been used since the
epoch of C++.

I don't consider it to be a hack, but an aid to documentation. The
reader can tell at a glance that the class isn't copyable. It also
saves typing!
 
J

Juha Nieminen

Pete said:
So the default copy constructor would only copy members that were not
copied by the user-written one? How could it know?

No, you first call the compiler-generated copy constructor, and then
you copy the special members appropriately.

For example, if you had, let's say, a pointer to a dynamically
allocated object, the compiler-generated copy constructor would simply
copy the value of the pointer, so the copy would point to the same
dynamically allocated object. However, that doesn't matter: Once it has
done that, you simply make a deep copy of the object (or whatever) and
make the pointer point to that new object.

The idea is that you only need to write this special copying code for
that one pointer, and you don't need to write anything for any of the
other member variables (because those are handled by the
compiler-generated copy constructor).
 
J

Juha Nieminen

Jeff said:
You have a reasonable point, but IMO such a class is already responsible
for too many things. If a member requires special handling during
copying, then it can be wrapped in a class whose copy constructor does
the right thing. That special handling should be the wrapper's only
job.

That's a reasonable design. On one hand it simplifies the outer class,
because you don't have to write a copy constructor for the entire class.
On the other hand it adds a small layer of complexity to that special
member. For example assume you have something like this:

class MyClass
{
std::list<Object> objectList;
std::list<Object>::iterator currentObject;
...
};

Let's assume the specification of this code is that 'currentObject'
always points to either an object inside 'objectList', or to
objectList.end().

This class requires a copy constructor and assignment operator if
instances of this class must be copyable. The copy constructor and
assignment operator need to make 'currentObject' to point to the copied
list (rather than the original).

By your suggested design both 'objectList' and 'currentObject' need to
be wrapped inside an inner class if we want to avoid having to write a
copy constructor and assignment operator on the outer class, so it would
become something like:


class MyClass
{
struct ObjectList
{
std::list<Object> objectList;
std::list<Object>::iterator currentObject;

ObjectList(const ObjectList&); // Implement these appropriately
ObjectList& operator=(const ObjectList&);
};

ObjectList objectList;
...
};

Now accessing the 'objectList' member has to always be done with a
"objectList.objectList" (or whatever we want to call the struct
instantiation). Well, I suppose that's a small price to pay for not
having to write a copy constructor and assignment operator for the outer
class...
 
J

James Kanze

A bit of clarity. A private constructor can be implemented,
and used by members and friends. "= delete" says it doesn't
exist.

And can't be used even by members and friends.

Not a big thing, but the fact that you cannot copy an object is
part of the public interface. So the fact that you explicitly
say so should logically appear in the public part of the class
definition.
 
J

James Kanze

That's true if the private constructors are declared
directly in the non-copyable class. I wonder whether
=delete offers anything over EBO/inheritance-based utilities
like boost::noncopyable.
[/QUOTE]
Elegance and simplicity. A private copy-constructor is a hack
but boost::noncopyable is much worse, a complex solution to a
simple problem. Why should I have to inherit from some other
class just to make my class non-copyable?

Documentation. Inheritance defines an isA relationship. Your
class isA UncopiableObject.
 
J

James Kanze

Agreed. It's self-documenting, and doesn't require the reader
to be familiar with yet another language feature.

Yes and no. I used the techique back in the early 1990's. I
dropped it for awhile because readers weren't familiar with it.
The private copy constructor was the standard idiom, which they
immediately recognized, where as they didn't know what
UncopiableObject did; at the time, at least, it didn't occur to
them that a base class might have not direct interface, and they
expected something more. Thanks to things like std::iterator<>,
std::unary_function<>, etc., modern C++ programmers are far more
open about this, and when they see a class called
UncopiableObject, they don't expect anything more than that it
can't be copied. Some I've reverted to doing things the way I
did 25 years ago.
 
P

peter koch

Testing will catch that.

Well - i certainly hope so ;-)
So the default copy constructor would only copy members that were not
copied by the user-written one? How could it know?

I agree - that would not be possible. But can I sidestep a little and
be off-topic for a second? I really like the idea of having a copy-
constructor being default being defined explicitly with the new
syntax, and now I wonder if there has been any proposal to expand on
the default stuff, notable for swap.
As the user-defined copy-constructor really is error-prone (I have
been bitten once or twice by forgetting the copying when adding a new
member to a class), I do my best to make sure that the default
constructor (and assignment-operator) works and normally restrict my
userdefined constructors to small classes with less than a handfull of
membervariables. But swap is another matter: I normally don't write a
swap function and live with the lost performance, correcting the
problem when e.g. I wind up with sorting large arrays of those
objects. But wouldn't it be nice if you could write void
swap(my_class& other) = default as well?
In other words: has there been any proposal to provide this
functionality in some later version of C++? I have skimmed the docs
but found nothing like that in C++0x. I do not know how general it
could be: having "= default" creating a function with standard
functionality for each member-variable (causing an error when that
functionality is not available) would be nice indeed.

/Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top