A question on "uncopyable" that disable copy constructor

J

Jorgen Grahn

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.

I see now that the rest of the posters in the thread focused on the
copy constructor. I got stuck on the default constructor, because
that's where I have problems.

Most of my classes are small, concrete classes and are naturally
copyable. Default copying is enough -- and come to think of it, if the
default wasn't enough, then it would probably not be one of those
classes I'd consider placing in a container!
Consider what will
happen if you later need to add a reference member or a managed resource
to your class.

I consciously try /not/ to think of such things. A future drastic
redesign will simply have to have repercussions. Hopefully it never
has to happen -- the copyability was part of the larger design, not
accidental.
Using shared_ptr<T> removes most of those issues.

I think that last issue is enough for me.

std::Vector<HomeForSale> homes = lots_of_homes();
assert(!homes.empty());
const HomeForSale& home = homes[0];
assert(home.valid());

is not much worse compared to

std::Vector<shared_ptr<HomeForSale> > homes = lots_of_homes();
assert(!homes.empty());
shared_ptr<HomeForSale> home = homes[0];
assert(home.get());

In both cases I cannot statically know that I can pull a valid home
from the non-empty vector.

/Jorgen
 
I

Ian Collins

Using shared_ptr<T> removes most of those issues.

I think that last issue is enough for me.

std::Vector<HomeForSale> homes = lots_of_homes();
assert(!homes.empty());
const HomeForSale& home = homes[0];
assert(home.valid());

is not much worse compared to

std::Vector<shared_ptr<HomeForSale> > homes = lots_of_homes();
assert(!homes.empty());
shared_ptr<HomeForSale> home = homes[0];
assert(home.get());

In both cases I cannot statically know that I can pull a valid home
from the non-empty vector.

You could prevent default initialisation of the pointer:

#include <vector>
#include <tr1/memory>

struct X {};

struct Ptr : std::tr1::shared_ptr<X>
{
explicit Ptr( X* x ) : std::tr1::shared_ptr<X>(x) {}

private:
Ptr() {}
};

int main() {
std::vector<Ptr> v(10);
}

Which is quite handy.
 
8

88888 Dihedral

在 2012å¹´2月26日星期日UTC+8上åˆ3æ—¶10分08秒,Juha Nieminen写é“:
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.



在 2012å¹´2月26日星期日UTC+8上åˆ3æ—¶10分08秒,Juha Nieminen写é“:
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));

I think here the index of the moved m in v should be saved in the next line. But it is possible that the vector v is used as a stack of the same type of objects.
 
P

Pavel

Jorgen said:
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.

...

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.
Maybe I am wrong but it seemed to me that Ian defended that "never" position.

His example with file descriptor as a simple object is very close to valid case
for this position (in theory, file handle wrappers can be copied if file
descriptor is dup()ed in copy constructor and dup()-ing is relatively
inexpensive on *NIX but I am not sure about other systems (Windows
DublicateHandle and WSADuplicateSocket in particular; also, that "relative" is
also relative..).

If in a context of a particular problem both correct and practical definition of
copying is *impossible* (as opposed to being just non-sensical for anything but
storing an object in a vector), "never" position can be justified. I think Ian
had that *practically impossible* problem in mind whereas your HomeForSale was
to illustrate a "nonsensical for anything but" case but please correct me if I
am wrong.

-Pavel
 
8

88888 Dihedral

Maybe I am wrong but it seemed to me that Ian defended that "never" position.

His example with file descriptor as a simple object is very close to valid case
for this position (in theory, file handle wrappers can be copied if file
descriptor is dup()ed in copy constructor and dup()-ing is relatively
inexpensive on *NIX but I am not sure about other systems (Windows
DublicateHandle and WSADuplicateSocket in particular; also, that "relative" is
also relative..).

If in a context of a particular problem both correct and practical definition of
copying is *impossible* (as opposed to being just non-sensical for anything but
storing an object in a vector), "never" position can be justified. I think Ian
had that *practically impossible* problem in mind whereas your HomeForSale was
to illustrate a "nonsensical for anything but" case but please correct me if I
am wrong.

-Pavel



Maybe I am wrong but it seemed to me that Ian defended that "never" position.

His example with file descriptor as a simple object is very close to valid case
for this position (in theory, file handle wrappers can be copied if file
descriptor is dup()ed in copy constructor and dup()-ing is relatively
inexpensive on *NIX but I am not sure about other systems (Windows
DublicateHandle and WSADuplicateSocket in particular; also, that "relative" is
also relative..).

If in a context of a particular problem both correct and practical definition of
copying is *impossible* (as opposed to being just non-sensical for anything but
storing an object in a vector), "never" position can be justified. I think Ian
had that *practically impossible* problem in mind whereas your HomeForSale was
to illustrate a "nonsensical for anything but" case but please correct me if I
am wrong.

-Pavel

Well, C and C++ are both imperative. But for those objects frozen,
a pointer or reference is enough to read the frozen object.

Thus, I distingush objects that could be modified by some methods in the run-time from those frozen ones that could be referenced by a pointer or a reference.
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top