A question on "uncopyable" that disable copy constructor

T

TH

Meyer's Effective C++ book talks about the case where you may want to disallow compiler generated copy constructor. The example is:

class HomeForSale {...}

It doesn't make sense to do:

HomeForSale h1(h2); or h1 = h2.

No problem. However, Does disallowing the copy constructor effectively prohibit it from putting such object into a container type, as

std::vector<HomeForSale> home_vec;
home_vec.push_back(h1); // need copy constructor??

It sounds like a severe constraints on the possible use of such object, am I missing anything?

Thanks

T.H.
 
I

Ian Collins

Meyer's Effective C++ book talks about the case where you may want to disallow compiler generated copy constructor. The example is:

class HomeForSale {...}

It doesn't make sense to do:

HomeForSale h1(h2); or h1 = h2.

No problem. However, Does disallowing the copy constructor effectively prohibit it from putting such object into a container type, as

std::vector<HomeForSale> home_vec;
home_vec.push_back(h1); // need copy constructor??
Yes.

It sounds like a severe constraints on the possible use of such object, am I missing anything?

The reason not to allow copying? If it is inappropriate or too
expensive to copy a T, use a container of (smart) pointers to T.
 
M

Miles Bader

TH said:
Meyer's Effective C++ book talks about the case where you may want to
disallow compiler generated copy constructor. The example is:

class HomeForSale {...}

It doesn't make sense to do:

HomeForSale h1(h2); or h1 = h2.

No problem. However, Does disallowing the copy constructor effectively
prohibit it from putting such object into a container type, as

std::vector<HomeForSale> home_vec;
home_vec.push_back(h1); // need copy constructor??

It sounds like a severe constraints on the possible use of such object,
am I missing anything?

A typical example of a class where you don't want an automatically
generated copy-constructor is one that has pointer member which is
deallocated by the destructor; simply copying the pointer member into
a new object would cause obvious problems (as soon as the first of the
two object is destroyed, the pointer would be dangling).

The key point is that _any_ use of a default copy-constructor for such
classes -- _including_ copying by std::vector or whatever -- will
violate the invariants the class relies on, and cause problems. So
suppressing it for all uses is a feature. :)

Of course, often one then provides a user copy-constructor instead,
which does the right thing for copying (e.g. making a copy of the
pointed-to object in the above example).

However in some cases that's hard or maybe just annoying to implement
(maybe you'd need to add reference counting for shared resources, etc,
and for the class in question, such complexity would be unjustified by
the expect use). Then you just disable the auto-generated constructor
and write in the documentation "don't copy this."

Yeah that means you can't put it in std::vector, etc...

However as the decision to make the object uncopyable is usually done
with an eye towards expected usage, presumably such a class is one
where "putting it in a vector" is not expected. If you _really_ need
a vector of such objects, you can use a vector of pointers or
something instead (obviously you need to be careful and manage the
lifetimes of the pointed-to objects, but oh well).

-miles
 
J

Jorgen Grahn

The reason not to allow copying? If it is inappropriate or too
expensive to copy a T, use a container of (smart) pointers to T.

It should be added though, that the uniqueness of a HomeForSale is not
enough reason to go crazy with smart pointers. If I saw a need to
place such a thing in a std::vector, I wouldn't hesitate to make it
copyable. I would also (reluctantly) add a default constructor if
needed, even though it means introducing "the nil home for sale".

/Jorgen
 
T

T.H.

A typical example of a class where you don't want an automatically
generated copy-constructor is one that has pointer member which is
deallocated by the destructor; simply copying the pointer member into
a new object would cause obvious problems (as soon as the first of the
two object is destroyed, the pointer would be dangling).

The key point is that _any_ use of a default copy-constructor for such
classes -- _including_ copying by std::vector or whatever -- will
violate the invariants the class relies on, and cause problems.  So
suppressing it for all uses is a feature.  :)

this "typical example" is very helpful to me as to when to consider
uncopyable object.

Thanks

T.H.
 
J

Juha Nieminen

bartek szurgot said:
std::vector<Movable> v;
Movable m;
v.push_back(m); // error - cannot copy
v.push_back( std::move(m) ); // ok - moves object

I'm not so sure doing that is such a good "pattern" because after
giving 'm' to the vector you are left with an "empty"/"null" 'm' which
has nothing in it (because what it had was moved to the vector).

A more sensible "pattern" would be to create a temporary that gets
destroyed immediately after its contents have been moved to the vector,
that is:

v.push_back(Movable());

OTOH, there might be some situations where you need to do it like your
version, for example like:

Movable m;
m.doSomething(abc);
m.doSomethingElse(def);
v.push_back(std::move(m));

However, in this case it might be worthy of consideration to move such
initialization to a function, so you would have something like:

Movable gimmeAMovable()
{
Movable m;
m.doSomething(abc);
m.doSomethingElse(def);
return m;
}
...
v.push_back(gimmeAMovable());

This way you don't end up with an "empty" object from which content has
been moved away.
 
I

Ian Collins

It should be added though, that the uniqueness of a HomeForSale is not
enough reason to go crazy with smart pointers. If I saw a need to
place such a thing in a std::vector, I wouldn't hesitate to make it
copyable. I would also (reluctantly) add a default constructor if
needed, even though it means introducing "the nil home for sale".

That's why I clearly said "If it is inappropriate or too expensive to
copy a T". Some things shouldn't, or simply can't be copied.
 
J

Jorgen Grahn

That's why I clearly said "If it is inappropriate or too expensive to
copy a T". Some things shouldn't, or simply can't be copied.

I know you did; I just added my view that conceptual uniqueness of
things like a HomeForSale does not make copying "inappropriate"
enough.

/Jorgen
 
P

Pavel

Juha said:
I'm not so sure doing that is such a good "pattern" because after
giving 'm' to the vector you are left with an "empty"/"null" 'm' which
has nothing in it (because what it had was moved to the vector).

A more sensible "pattern" would be to create a temporary that gets
destroyed immediately after its contents have been moved to the vector,
that is:

v.push_back(Movable());
They've got this, too, in C++11 (some useful new stuff for a change):

v.emplace(v.end(), EmplaceConstructible());
....

HTH
-Pavel
 
J

Juha Nieminen

Pavel said:
v.emplace(v.end(), EmplaceConstructible());

Doesn't that call the copy constructor of EmplaceConstructible (which
kind of defeats the whole idea of having a non-copyable class)?

If I understand the documentation correctly, if you wanted to insert
a default-constructed element you would do it like:

v.emplace(v.end());

and if you wanted to give the constructor parameters, you simply give
them as parameters to emplace(), like:

v.emplace(v.end(), 1, 2, 3);

(Perfect forwarding rulez.)
 
N

none

I know you did; I just added my view that conceptual uniqueness of
things like a HomeForSale does not make copying "inappropriate"
enough.

I am quite willing to agree with the idea that conceptual uniqueness
may not necessarily require to actually make the object non-copyable.
If there's really a need for the object to be copied, then by all
mean, go for it and figure out a different way to preserve uniqueness.

I am however concerned with the statement "go crazy with smart
pointer". Are you suggesting that:

std::vector< std::shared_ptr<HomeForSale> > home_vec;

is going crazy?

Yannick
 
J

Jorgen Grahn

I am quite willing to agree with the idea that conceptual uniqueness
may not necessarily require to actually make the object non-copyable.
If there's really a need for the object to be copied, then by all
mean, go for it and figure out a different way to preserve uniqueness.

I am however concerned with the statement "go crazy with smart
pointer". Are you suggesting that:

std::vector< std::shared_ptr<HomeForSale> > home_vec;

is going crazy?

How could I tell without context?

I'm just saying that if std::vector is the only reason to add a
default constructor and copy constructor to HomeForSale, then I curse
under my breath, and then find a way to implement and document them.
I wouldn't want to see any kind of pointer used /just to avoid that/.

/Jorgen
 
N

none

How could I tell without context?

I'm just saying that if std::vector is the only reason to add a
default constructor and copy constructor to HomeForSale, then I curse
under my breath, and then find a way to implement and document them.
I wouldn't want to see any kind of pointer used /just to avoid that/.

I still don't follow you.

We have the HomeForSale class. It is non-sensical to copy a
HomeForSale and the concept a a default HomeForSale is also
non-sensical (we don't live in dark utopia of default homes :)

But /just to avoid using any kind of pointers/, you find a way to
implement copying and default construction that you then need to
document to explain to future generation that they should never be
used per se and that they are only a hack to fullfill the std::vector
requirements...

Why is using "any kind of pointer" such a bad thing that it is worth
jumping through hoop to avoid doing it?

Yannick
 
J

Jorgen Grahn

I still don't follow you.

We have the HomeForSale class. It is non-sensical to copy a
HomeForSale and the concept a a default HomeForSale is also
non-sensical (we don't live in dark utopia of default homes :)

But /just to avoid using any kind of pointers/, you find a way to
implement copying and default construction that you then need to
document to explain to future generation that they should never be
used per se and that they are only a hack to fullfill the std::vector
requirements...

That's right. I assume most people do it that way.
(If most people here don't, I'd like to hear about it!)
Why is using "any kind of pointer" such a bad thing that it is worth
jumping through hoop to avoid doing it?

I don't enjoy doing it, but "jumping through hoops" is overstating the
problem.

Performance, convoluted syntax, manual memory management. Probably
many other reasons too which I haven't thought of because I haven't
encountered those problems.

I'm also not sure that I'd prefer finding a null pointer in my array,
compared to finding a nil HomeForSale.

/Jorgen

PS. I'm talking C++98 here. If any new C++11 features help here, I
haven't checked them out yet.
 
I

Ian Collins

That's right. I assume most people do it that way.
(If most people here don't, I'd like to hear about it!)

Well I wouldn't. I hardly ever store Ts in a vector. More often than
not I use a container of shared_ptr<T>. Adding a copy constructor when
your class design doesn't warrant one is poor form. Consider what will
happen if you later need to add a reference member or a managed resource
to your class.
I don't enjoy doing it, but "jumping through hoops" is overstating the
problem.

Performance, convoluted syntax, manual memory management. Probably
many other reasons too which I haven't thought of because I haven't
encountered those problems.

I'm also not sure that I'd prefer finding a null pointer in my array,
compared to finding a nil HomeForSale.

Using shared_ptr<T> removes most of those issues.
 
P

Pavel

Juha said:
Doesn't that call the copy constructor of EmplaceConstructible (which
kind of defeats the whole idea of having a non-copyable class)?
True, that was the wrong syntax.
If I understand the documentation correctly, if you wanted to insert
a default-constructed element you would do it like:

v.emplace(v.end());

and if you wanted to give the constructor parameters, you simply give
them as parameters to emplace(), like:

v.emplace(v.end(), 1, 2, 3);

(Perfect forwarding rulez.)

-Pavel
 
P

Pavel

Ian said:
Well I wouldn't. I hardly ever store Ts in a vector. More often than not I use a
container of shared_ptr<T>. Adding a copy constructor when your class design
doesn't warrant one is poor form. Consider what will happen if you later need to
add a reference member or a managed resource to your class.

I think this conversation would win from agreeing on some context, namely a
particular reason why you do not need copy constructor. As an example, if all
one needs from an object (in terms of creation/destruction) is to keep a
collection of them in an array and a standalone object is of no value (it's an
often situation in my practice when you need to dispatch messages to processors
and the processors are relatively rarely created/removed so you want to dispatch
in amortized constant time), std::vector comes to mind. Now it's up to you to
decide whether you introduce copy constructor or create a vector of pointers. If
objects are very small (say, these are some stateless policies or policies
thousands of which may share the state) but there are tons of them, vector of
pointers may create a significant waste, even if you use some pool allocator
(because in addition to an object, which is, say 4-8 bytes, you would have to
keep, say, 4-byte pointers in a vector -- this is a meaningful overhead). There
is also an additional indirection cost (which may, in some patterns, almost half
the effective CPU cache size).

Thus, you are facing a tradeoff: add a copy constructor (unnecessary copying in
this case is the only price; and does not seem significant because the objects
are relatively long-living; not much ideology/design purity considerations are
involved either IMHO) vs vector-of-pointers (with its meaningful additional
space and time costs).

Before C++11, I would probably add a copy constructor (in this particular
context). In C++11, emplace() gives you best of two worlds.

There can certainly be other considerations. For example, add some extra
reference to the message-dispatching processors described above ("extra" in a
sense that it is in addition to those kept by message sources (or logical
connections to them)) and you would be happy if you originally chose shared
pointer design.

IMHO, programming is all about applying abstract ideas to a concrete problem;
and the "concrete problem" part of the equation is at least as important as are
the abstract ideas. It often puzzles me when someone starts an argument on which
abstract idea is "better" without first agreeing on a meaningful context to
apply it.

.....

-Pavel
 
I

Ian Collins

I think this conversation would win from agreeing on some context, namely a
particular reason why you do not need copy constructor. As an example, if all
one needs from an object (in terms of creation/destruction) is to keep a
collection of them in an array and a standalone object is of no value (it's an
often situation in my practice when you need to dispatch messages to processors
and the processors are relatively rarely created/removed so you want to dispatch
in amortized constant time), std::vector comes to mind. Now it's up to you to
decide whether you introduce copy constructor or create a vector of pointers. If
objects are very small (say, these are some stateless policies or policies
thousands of which may share the state) but there are tons of them, vector of
pointers may create a significant waste, even if you use some pool allocator
(because in addition to an object, which is, say 4-8 bytes, you would have to
keep, say, 4-byte pointers in a vector -- this is a meaningful overhead). There
is also an additional indirection cost (which may, in some patterns, almost half
the effective CPU cache size).

I agree with all of the above.

Most of the programming I do these days is either XML document
processing or systems work. Both of these usually involve searching,
sorting and manipulating trees of objects. Thus each "Node", even in
its most basic form contains a vector of children and a vector of
attributes. So even though the objects are fairly small, copying them
can be very expensive.

In the case of XML DOM, the interface requires a pointer based solution.
All access and search functions return references to or lists of nodes
with the document.

With filesystem data and meta-data, I often have to present several
different views of the data, which more often than not is some form of
tree. Here containers of T* or shared_ptr<T> are the logical choice.

So I frequently use the PIMPL idiom, where the "Node" class extends
shared_ptr with the member functions required by the object.
Thus, you are facing a tradeoff: add a copy constructor (unnecessary copying in
this case is the only price; and does not seem significant because the objects
are relatively long-living; not much ideology/design purity considerations are
involved either IMHO) vs vector-of-pointers (with its meaningful additional
space and time costs).

Don't forget there are often simple objects (say wrapper round a file
handle) that are simply inappropriate to copy.
Before C++11, I would probably add a copy constructor (in this particular
context). In C++11, emplace() gives you best of two worlds.

I don't think id would be appropriate for object were copying is
inappropriate, or objects that hold containers to other objects.
There can certainly be other considerations. For example, add some extra
reference to the message-dispatching processors described above ("extra" in a
sense that it is in addition to those kept by message sources (or logical
connections to them)) and you would be happy if you originally chose shared
pointer design.

IMHO, programming is all about applying abstract ideas to a concrete problem;
and the "concrete problem" part of the equation is at least as important as are
the abstract ideas. It often puzzles me when someone starts an argument on which
abstract idea is "better" without first agreeing on a meaningful context to
apply it.

Very true. When dealing with physical entities, copying them often
isn't appropriate
 
J

Jorgen Grahn

I think this conversation would win from agreeing on some context, namely a
particular reason why you do not need copy constructor.

I think the discussion shifted subtly and because of that became less
understandable. I don't mind keeping the default copy constructor for
things which are naturally copyable. The default constructor was, to me,
the real issue here.

....
IMHO, programming is all about applying abstract ideas to a concrete problem;
and the "concrete problem" part of the equation is at least as important as are
the abstract ideas. It often puzzles me when someone starts an argument on which
abstract idea is "better" without first agreeing on a meaningful context to
apply it.

I don't think that was the case here. The HomeForSale example was
concrete enough -- and I don't think anyone suggested you should
/always/ or /never/ use containers of pointers, no matter what the
actual problem is.

/Jorgen
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top