How to Make This C++ Object Non-Copyable

How do I make this C++ object non-copyable?

class Foo {
private:
Foo();
Foo( const Foo& ); // non construction-copyable
Foo& operator=( const Foo& ); // non copyable
public:
static Foo* create();
}

If you're using boost, you can also inherit from noncopyable : http://www.boost.org/doc/libs/1_41_0/boost/noncopyable.hpp

EDIT: C++11 version if you have a compiler supporting this feature:

class Foo {
private:
Foo();
public:
Foo( const Foo& ) = delete; // non construction-copyable
Foo& operator=( const Foo& ) = delete; // non copyable

static Foo* create();
}

Note that deleted methods should be public: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-delete

Make a class non-copyable *and* non-movable

As others already mentioned in the comments, deleted constructors was introduced in C++11. To answer your question, the following rules hold in general:

  1. The two copy operations are independent. Declaring copy constructor does not prevent compiler to generate copy assignment and vice versa. (Same as in C++98)
  2. Move operations are not independent. Declaring either one of them prevents the compiler to generate the other. (Different from copy operations.)
  3. If any of the copy operations is declared, then none of the move operation will be generated. (Your case.)
  4. If any of the move operation is declared, then none of the copy operation will be generated. This is the opposite rule of the previous.
  5. If a destructor is declared, then none of the move operation will be generated. Copy operations are still generated for reverse compatibility with C++98.
  6. Default constructor generated only when no constructor is declared. (Same as in C++98)

As requested in the comments, here are some sources (C++11 is draft N3242):

  • Copy operations: § 12.8.8, § 12.8.19
  • Move operations: § 12.8.10, § 12.8.21
  • Default constructor: § 12.1.5

Can a non-copyable member be used as alternative to make an object non-copyable?

Since C++11, the proper idiom for making a class non-copyable is to = delete the copy constructor/assignment operator. That's what C++ programmers are told to do, and that's what other C++ programmers will expect to see when looking for that behavior in your class..

It's fine to have a subobject (member or base class) that is non-copyable, and thus your default copy constructor/assignment operator will be implicitly deleted. But you should only do this for a subobject that happens to be non-copyable. That is, you have a unique_ptr<T> or mutex or whatever as a member because you need a unique_ptr<T> or a mutex as class instance data. Not because you're using it as a hack to make the type non-copyable.

The downsides of using a member subobject for this purpose are:

  1. It muddles the meaning of your code. Your mutex _dummy; example tells me that your type has a mutex in it. If nothing ever uses that variable, then that tells me that your code is rather incoherent; if you don't need a subobject, you don't declare one. = delete is the proper idiom, so it is what you should use.

  2. boost::noncopyable was the C++98/03 idiom because it was an empty class. And thus, common empty base optimization would ensure that it wouldn't take up any space in the derived class. Empty members get no such optimization, so a member boost::noncopyable will always make your class bigger, to no advantage. And while you could point to the upcoming C++20 [[no_unique_address]] attribute, see reason #1.

how to initialize a non copyable object data member

To make the above compile:

Add the headers:

#include <string>
#include <memory>

You also need to be able to construct the A so you need to make the constructors public:

class A //abstract class
{
public: // Added this:
A() = default;
.....
};

Your main issue is here in making the shared object.

 obj2(std::make_shared<SI::Z>(SI::Z(p,"hi")))

You don't need to constructed the SI::Z object here (as it is not copyable this is an issue). What you want to do is pass the arguments that will be used to create the SI::Z object. Then std::make_shared() will call new and forward these parameters to the constructor.

 obj2(std::make_shared<SI::Z>(p, "hi"))     // Notice the diff?

Non-copyable struct in C?

You can do this using opaque pointers. The idea is:

  • You define a struct somewhere and you define all of its operations in terms of a pointer to that struct. That would probably be a standalone compilation unit.
  • The consumers of your struct only get a declaration but not the full definition of that struct, which means that they don't know the layout or even the size of the struct. It follows that they are able to receive, store, and pass around any pointers to that struct, but not values of it.

How to handle classes that are not copyable?

Moveable types

You can make the class moveable by adding the move constructor and move assignment operator. (Some possibly useful overview of that.)

...
// rough sketch
// Having the path as `char*` seems
// bad (use std::string instead),
// but let's stick with the example as posted)
File(File&& other)
: path(nullptr)
, cfile(nullptr)
{
using std::swap;
// steal the guts of the moved-from object
swap(path, other.path);
swap(cfile, other.cfile);
}

The move assignment operator can be a little bit more tricky, what with self-assigment checks.

/or/

Smart pointers

You can wrap instances of these classes in a smart pointer and work with those:

  • std::shared_ptr, std::unique_ptr etc.
    std::unique_ptr<File> Search(const char* filepath) {
auto pfile = std::make_unique<File>(filepath);
...
return pfile;
}

This is especially helpful when you work with 3rd party classes (possibly legacy) that don't support move operations.

Yes, these have heap allocation overhead, but given the overhead of opening e.g. a file or acquiring another expensive resource that prevents copying, the heap allocation overhead might well be negligible.

Is it possible to make an noncopyable type in C#

It is default behavior. If you have class:

class foo {
public string bar { get; set; }
}

and somewhere you do this:

foo f1 = new foo();
foo f2 = f1;

Both f1 and f2 will reference same instance. If you, for example, set f1.bar = "bar", value read from f2.bar will be "bar".

Returning non-copyable non-movable object with explicit constructor

No, this isn't possible. There is no mechanism to call explicit constructors while returning from a function in C++11 or 14 without having an implicit move or copy (that the compiler will certainly elide).

In C++17 you can just type return NonCop(1,2); and due to "guaranteed elision" it will no longer require a move or copy constructor.


But this is C++, so yes, I can make your code work with zero additional overhead. By cheating, and returning a different type.

template<class T>
struct implicit_construct:T {
template<class...Ts>
implicit_construct(Ts&&...ts):
T(std::forward<Ts>(ts)...) // note: this is an EXPLICIT construction
{}
};

implicit_construct<NonCop> get_non_cop()
{
return {1, 2};
}

Live example.

An implicit_construct<NonCop> derives from a NonCop, so you can store the return value in a NonCop&&.


If you are writing NonCop yourself, then what I'd do is add:

 struct explicit_construct_t {};
// ...
struct NonCop {
// ...
template<class...Ts>
NonCop( explicit_construct_t, Ts&&...ts ):
NonCop( std::forward<Ts>(ts)... )
{}
// ...
};

which means you can call explicit constructors by prefixing it with a explicit_construct_t to call them implicitly:

NonCop get_non_cop() {
return {explicit_construct_t{}, 1, 2};
}


Related Topics



Leave a reply



Submit