1

Before anything else, let me take you all into the highway of my thoughts (to say it simply, I'm just imagining these things)

Suppose, I am using a third-party library class that uses move semantics (r-value references). Here is its definition:

class VeryHeavyObject {
    VeryHeavyObject(const VeryHeavyObject& obj); // copy constructor
    VeryHeavyObject& operator= (const VeryHeavyObject& obj); // copy operator

public:
    VeryHeavyObject(); // constructor

    VeryHeavyObject(VeryHeavyObject&& obj); // move constructor
    VeryHeavyObject& operator= (VeryHeavyObject&& obj); // move operator

    // ....
};

Apparently, the author's really concerned about the cost of copying of VeryHeavyObject and decided to force everything to be moved (More apparently, he doesn't know how to design classes with move semantics). But then, in some point of my code, I need to have a copy of VeryHeavyObject.

Well, the core question is: How can I copy an object with only a move constructor and a move operator?

P.S.: I have tried but I can't really contact the author of the library (I think he's on vacation).

Mark Garcia
  • 17,424
  • 4
  • 58
  • 94
  • Is there a complete interface of VeryHeavyObject? There may be a `.clone()` method. – kennytm Nov 28 '12 at 09:14
  • @KennyTM Maybe the author's just don't know how to design the class and forgot a clone method. (Please do get my point) – Mark Garcia Nov 28 '12 at 09:16
  • 6
    Is it because the object is heavy, or because it should really not be copied (e.g. it represents a unique resource)? – Angew is no longer proud of SO Nov 28 '12 at 09:18
  • 2
    Why do you need to copy it in the first place? May be it's not necessary. e.g. use `emplace_back` instead of `push_back` for stl containers... –  Nov 28 '12 at 09:18
  • I don't get it... can't you just change the header file and put in some inline copy constructors/assignments? – user541686 Nov 28 '12 at 09:20
  • Guys! Please! Do try to get my point. I'm after on the specific hows and the the overall design of the (imaginary) software. – Mark Garcia Nov 28 '12 at 09:20
  • 4
    Note: in C++11 you should use `= delete` to remove methods, rather than just making them `private` (which provoke a link error if they are still used). But then it is moot because §12.8/7 precises *If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted;*, so you're doing extra work which hampers the compiler's diagnostics ;) – Matthieu M. Nov 28 '12 at 09:28
  • @MatthieuM. So it means you have to *explicitly* define the copy constructor. Right? – Mark Garcia Nov 28 '12 at 09:29
  • @MarkGarcia: Depends on the objective. If you provide a move constructor or move assignment operator AND you still want a copy constructor, then yes you have to explicitly define your copy constructor. On the other hand, note that `Type(Type const&) = default;` count as explicit definition :) – Matthieu M. Nov 28 '12 at 10:11

4 Answers4

6

You cannot.

However, provided that you have sufficient access to its internals (getters and the like), then you can construct a clone by yourself.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

A well defined interface, and we will assume that this is the case, some methods may not be available because the author wants to discourage certain uses for performance reasons. A well-known example is std::list, which does not include a [] operator because it has O(n) complexity compared with O(1) in other containers, such as std::vector.

In this case, the author of the library wants to discourage the use of copies because, as you state in your question, it is very costly. But this does not mean that it is impossible. If you really need to do it, you can probably write your own Clone() function that takes data from the original VeryHeavyObject as appropriate, constructs a new one with these data and returns it using std::move. Since we haven't got the interface for VeryHeavyObject we cannot try to do it, but I'm sure you can.

Gorpik
  • 10,940
  • 4
  • 36
  • 56
  • I want to do it *without* modifying the class's interface. – Mark Garcia Nov 28 '12 at 09:26
  • @MarkGarcia: you misread, Gorpik never mentionned modifying the class interface. – Matthieu M. Nov 28 '12 at 09:29
  • @MarkGarcia Matthieu is right, I'm not asking you to modify the class interface, but to add a free function to do the cloning. Which is more or less what he himself suggested in his accepted answer, so that's fine for me. – Gorpik Nov 28 '12 at 12:58
2

It might not be possible.

The class has declared the copies private, but we can't see whether the functions are ever defined. You seem to assume that the class has a copy operation that it's hiding away from you to stop you doing something slow, but that might not be the case. Some objects simply cannot be copied. For examples, consider streams.

You wouldn't expect privately-declared-but-not-defined functions in C++11, but there's no law against it. Anyway even if there is an implemented private copy function, it's probably private for a reason (maybe it can only be used under certain controlled circumstances: the class internals know how to use it safely and you don't). So if there's no public copy, then as far as this class's API is concerned it cannot be copied.

Perhaps the class has enough public accessors, that you can interrogate it for the state you need, and construct a new object that matches it. If so then you could reasonably complain to the author of the class that it should be publicly copyable. If not then maybe it has state that can't be duplicated.

Anything that provides unique access to something (streams, drivers, locks) has a reason not to be copyable, because the original and the copy can't both provide unique access to the same thing. Admittedly dup means that even file descriptors don't physically provide unique access to something, let alone the streams that wrap them. But a stream has state involving buffered data not yet written, which means that copying them would introduce complexity that the class is designed to protect you from. So logically you normally use a stream as though it is the only way to access something.

If the copy assignment operator is implemented, then you might be able to hack a way to call it even though it's private. That won't work for the copy constructor, though, you can't take pointers to constructors. As a brutal hack you could #define private public before including its header: it's undefined behavior but it might work on the implementation you're using. Forking the third-party source would be better.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • So there's no (at least portable) way to copy such objects. – Mark Garcia Nov 28 '12 at 10:09
  • @MarkGarcia: there's no general way to copy any such object, no. You have to look at the particular class, work out why it isn't publicly copyable, and then decide whether you or the author can do anything about that. – Steve Jessop Nov 28 '12 at 10:12
1

In general, it is not possible without modifying the class, because there might be private data that you cannot access. It might be possible if a shallow copy is sufficient, because then you should be able to do it with a memccpy. (Note that if the class does not have any virtual members or pointers, shallow and deep copy are the same).

Community
  • 1
  • 1
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • 2
    I consider it extremely unlikely that an object which can be moved efficiently can be copied bitwise without wreaking havoc on the copy once the original has been destroyed. – fredoverflow Nov 28 '12 at 09:46