Prior to C++11, I saw code like this:
class Car {
public:
Car() {}
private:
Car(const Car&);
Car& operator=(const Car&);
};
For C++11 (and later), I see code like this:
class Car {
public:
Car() {}
private:
Car(const Car&) = delete;
Car& operator=(const Car&) = delete;
};
Do they behave identically? If not, please explain.
Ref: https://ariya.io/2015/01/c-class-and-preventing-object-copy
They are similar in most respects, but differ in some others.
Consider the following external code trying to copy objects of the class:
int main() {
Car c;
Car other{c};
}
Both of these versions will cause the above code to fail. At this point, though, the developer would look at the interface to see why. With the delete version, it's obvious that Car was not meant to be copied. With the private version, (at least without a comment), it raises a doubt whether the copy constructor was perhaps placed in the private section by accident.
Now consider member code trying to copy objects of the class:
void Car::foo() {
Car c;
Car other{c};
}
The delete version fails like before. The private version is a link error. This raises an even greater doubt - it's not uncommon to forget to define a method that has been declared. Perhaps this is what's happened here? The delete version doesn't have this problem.
Edit Scott Meyers discusses this in Item 11 of Effective Modern C++ Prefer deleted functions to private undefined ones.
=delete will give more meaningful error messages. An error message about a deleted function tells you that it doesn't exist and no one can create an object with said constructor. Saying it is private doesn't give that information - it just says the caller cannot call it, not that no one can.
Also, a deleted constructor will not behave differently if called from within/outside the class (since private constructors can be called from within the class).
https://godbolt.org/g/06R9AQ
Note that the two snippets you posted give exactly the same error, that is something like:
'Car(const Car&)' is private within this context
This is because you defined the member methods as private in both cases.
If you want to appreciate the differences, you should rather have public deleted copy constructor and copy operator, that is:
class Car {
public:
Car() {}
// private: <-- this should not be here
Car(const Car&) = delete;
Car& operator=(const Car&) = delete;
};
This way, you will be informed that those member methods have been explicitly and intentionally deleted:
use of deleted function 'Car(const Car&)'
Setting them as private doesn't say explicitly I want to delete them.
As an example, it could have been done for you want to force the users of your class to use a factory method to create instances of that class.
Anyway, (no longer so) new features are not for free and using them in a way that is not the intended one won't give the expected benefits.
Car& operator=(const Car&) = delete; is expressing explicitly "copy assignment prohibited".
= delete; can also be used for any function as described on Bjarne's blog:
struct Z {
// ...
Z(long long); // can initialize with an long long
Z(long) = delete; // but not anything less
};
Related
I'm trying to reset a full class with a virtual dtor.
Class Foo has lot's of data members, it would be easier to reset them all at once
class Foo : public QOject
{
public:
Foo() = default;
Foo(Foo&& rhs) = default;
Foo(Foo& rhs) = default;
Foo& operator=(const Foo& rhs) = default;
Foo& operator=(Foo&& rhs) = default;
private:
///lots of data members here
}
Since QOject has a virtual dtor no default constructors/operators are generated for Foo
When calling
mg_foo = Foo();
the compiler fails to generate a copy operator because QObject has virtual functions
I can't use pointers as well since at application startup I'm passing a raw address to QML
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("foo", &mg_foo);
If I use a pointer and change the address of mg_foo at runtime (with a brand new initialised class) my application crashes.
How would you handle such problem?
Check the QT documentation for the QObject class:
QObject has neither a copy constructor nor an assignment operator. This is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The reasoning is found in the discussion on Identity vs Value on the Qt Object Model page.
The main consequence is that you should use pointers to QObject (or to your QObject subclass) where you might otherwise be tempted to use your QObject subclass as a value. For example, without a copy constructor, you can't use a subclass of QObject as the value to be stored in one of the container classes. You must store pointers.
So the compiler will fail to generate the copy and assignment operators no matter what you do. You might also want to note that the QObject's only constructor is QObject::QObject(QObject* parent=nullptr). Looking at the documentation, this means that every instance of Foo you create (with your compiler-generated default constructor) will be a top-level window.
As for what to do regarding "resetting" the class, you will have to be more specific about what you are looking for there.
If I understand the problem well, resetting the many data members should be very easy. Just put all the "many" data members to a private data class/struct.
class Foo : public QObject
{
public:
// ... public stuff
void reset();
private:
struct FooPrivate
{
// all the "many" data members with their default values
};
FooPrivate m_data;
};
void Foo::reset()
{
m_data = FooPrivate{}; // this resets the data
}
Yes the drawback is that you have to access all the members via m_data. But it is the price you pay for that you can reset the data in one line.
Btw. If I had too many data members in my class I would think twice about the design. It suggests that the class may have too much responsibility. And you know the rules: one class, one responsibility. Maybe you should change the overall design and split the class into two or more.
I mostly work on system-level C++ projects that don't allow exceptions to be thrown, but RAII is (rightfully) strongly encouraged. Right now, we handle the lack of failing constructors using infamous tricks many C++ programmers are familiar with, like:
Trivial constructor followed by a call to bool init(Args...) to do the hard stuff
Real constructor followed by checking bool is_valid() const
Heap-allocating with static unique_ptr<MyType> create(Args...)
Of course, these all have drawbacks (heap allocation, invalid and "moved" states, etc).
My company is finally updating compilers and will allow glorious C++17 to be used. Since C++17 features std::optional<T> and, most importantly, mandatory copy elision, I was hoping I could greatly simplify all our classes into something that would look like this:
class MyType {
public:
static std::optional<MyType> create() {
// If any of the hard stuff fails, return std::nullopt
return std::optional<MyType>(std::in_place, 5, 'c');
}
~MyType() {
// Cleanup mArg0 and mArg1, which are always valid if the object exists
}
// ... class functionality ...
// Disable default constructor, move, and copy.
// None of these are needed because mandatory copy elision
// allows the static function above to return rvalue without
// copy or move operations
MyType() = delete;
MyType(const MyType&) = delete;
MyType(MyType&&) = delete;
MyType& operator=(const MyType&) = delete;
MyType& operator=(MyType&&) = delete;
private:
MyType(ArgT0 arg0, ArgT1 arg1) : mArg0(arg0), mArg1(arg1) {}
ArgT0 mArg0;
ArgT1 mArg1;
};
Notice how nice this is: Static function ensures all the hard stuff is done before the object is ever created, lack of default ctor/move means object never exists in an invalid or moved state, private constructor ensures user can't accidentally skip the named ctor.
Unfortunately, because the ctor is private, the std::is_constructable_t<MyType> check fails and therefore the in_place constructor of optional is SFINAE'd out.
This code works if I do one of 2 things, neither of which I want to:
Make the ctor public (But now users of the class can accidentally circumvent the named ctor)
Allow the move operations (But now I have to deal with invalidated objects)
I have also tried this, but it doesn't work because std::optional required a move operator for this to work:
static std::optional<MyType> create() {
// If any of the hard stuff fails, return std::nullopt
return std::optional<MyType>(MyType(5, 'c'));
}
Is there some trick or incantation I may be missing to get this to work, or have I hit the limits of what C++17 will allow?
Thanks!
If you want to make any indirect object construction work (emplace in its various forms, in_place constructors of optional, make_shared, etc) , the constructor in question must be public. You can make a constructor public without allowing all public use by using something called a private key.
Basically, you create a type (call it Key) whose default constructor is private. The class has no members, nor does it do anything. It declares that MyType is a friend of Key; this means that only members of MyType can construct one.
Now, make all of MyType's constructors public, but they all take a Key const& as the first parameter. This means that in theory anyone could call them, but in practice only someone who has a Key instance can actually call them. Members of MyType can create such an instance, and they can pass those instances to optional's in_place constructor or any other indirect mechanism. This effectively gives the indirect construction mechanism private access to the constructor.
This is a standard idiom for dealing with forwarding of private access to a type. Indeed, one could hypothetically write a generic key<T> type like this:
template<typename T>
class key
{
private:
key() = default;
key(int) {} //Not an aggregate
friend T;
};
One small note. Because of an annoyance of C++11 pre-C++20, any type with no members and no constructors other than defaulted/deleted copy/move/default constructors is considered an aggregate. This is true even if you explicitly = default its default constructor. As such, that type can undergo aggregate initialization, which has no public/private distinction. That is, anybody could call your private-key constructors by doing this: MyType({}, <params>);.
To avoid this, you will need to give Key an additional (private) constructor or otherwise prevent it from being an aggregate.
I cannot understand the rationale behind the automatic addition of default ctors. In particular I find very awkward that every single time I just need to add an empty virtual destructor and nothing more, I loose the move stuffs, but adding them I loose the copy and default things, so I end up adding all this chunk of code:
virtual ~SomeClass(){} // you are the guilty!
//virtual ~SomeClass() = default // would be the same
SomeClass(SomeClass&&) = default; // no more auto-added
SomeClass& operator=(SomeClass&&) = default; // no more auto-added
SomeClass(const SomeClass&) = default; // but with the moves defined,
SomeClass& operator=(const SomeClass&) = default; // I'm now missing the copy
SomeClass(){} // and the default as well
I'm sure there is a reason for making my classes ugly and letting me desire an evil macro, I just would like to know it to feel more comfortable.
Take a look at this. It explains something called the rule of five, which is essentially what standard requires.
Generally, for most cases, compiler creates defaults for copy constructor, copy assignment, move assignment, and destructor. But, if a programmer defines any of these, then the compiler assumes the user has encapsulated something in this class that requires his/her special, let's say. destructor. Now that the programmer knows that he/she is going to need a destructor, the compiler will know that the programmer know what's going on and just not create the defaults for the rest (because, based on the assumption that the compiler makes, the default ones are going to be wrong, and can even result in undesired behavior).
The problem is that your class is trying to do two separate things: providing a polymorphic interface (hence the need for the virtual destructor) and managing concrete data members (hence the need for the copy/move operations). It's generally a good idea to give each class a single responsibility.
I'd move the virtual destructor, and any virtual function declarations, to an empty, abstract base class. Then any concrete class(es) deriving from that will be free to autogenerate all the needful things.
Example:
#include <iostream>
struct Movable {
Movable() {}
Movable(Movable&&m) {std::cout << "Moving\n";}
};
struct SomeInterface {
virtual ~SomeInterface() {}
// no data members, so no need for any other special member functions
};
struct SomeClass : SomeInterface {
Movable stuff;
// no user-declared special functions, so all are auto-generated
};
int main() {
SomeClass c;
SomeClass c2(std::move(c)); // uses the auto-generated move constructor
}
Is there a way to achive that behaviour on compilers that don't support C++11 ?
class Meow
{
public:
Meow(const Meow&) = delete;
};
Making the constructor private is not a solution, because then you can do something like that:
class Meow
{
private:
Meow(const Meow&);
public:
Meow();
void doSomething()
{
Meow kitty;
Meow secondKity(kitty); // allowed
}
};
If the constructor is marked as deleted the above is not possible.
Making the constructor private is the pre-C++11 solution. Your second code is not valid because the copy constructor doesn't have a definition (presuming you don't give it a definition elsewhere). Yeah, it's not the best solution, but that's why = delete was introduced.
You may want to use boost::noncopyable to be more explicit about it, but it only does the same thing.
You can easily emulate that with a private, not defined copy constructor.
Instead of a nice error message you'll get a linker error as the copy constructor won't be found.
Another alternative is boost::noncopyable (http://www.boost.org/doc/libs/1_55_0/libs/utility/utility.htm#Class_noncopyable), which wraps this technique in a nice little helper class.
Suppose I have a class where the copy constructor is private and not implemented (to make the object non-copyable)
class NonCopyable {
// whatever
private:
NonCopyable( const NonCopyable&);
void operator=(const NonCopyable&);
};
Now in some member function of the same class I write code that returns an object of that class:
NonCopyable NonCopyable::Something()
{
return NonCopyable();
}
which is a case when RVO could kick in.
RVO still requires that a copy constructor is accessible. Since the possible call to the copy constructor is done from within the same class member function the copy constructor is accessible. So technically RVO is possible despite the fact that the intent was to prohibit using the copy constructor.
Is RVO allowed in such cases?
Yes, RVO would be allowed in this case - at least if the caller of Something() was a class member or friend.
I think this is one reason why private inheritance of a non-copyable class is better than doing it 'manually' in each class you want to prevent copying in. In that case there's no accidental loophole.
For example, using boost::noncopyable:
class NonCopyable : private boost::noncopyable {
public:
NonCopyable() {};
NonCopyable Something();
};
NonCopyable NonCopyable::Something()
{
return NonCopyable(); // causes compile time error, not link time error
}
Your example is quite interesting.
This is the typical C++03 declaration.
class NC {
public:
NC NC::Something() {
return NC();
}
private:
NC(NC const&);
NC& operator=(NC const&);
};
Here, as noted, RVO may kick in even though we semantically wanted to avoid copying.
In C++03, the solution is to delegate:
class NC: boost::noncopyable {
public:
NC NC::Something() { // Error: no copy constructor
return NC();
}
};
In C++11, we have the alternative of using the delete keyword:
class NC {
public:
NC NC::Something() { // Error: deleted copy constructor
return NC();
}
private:
NC(NC const&) = delete;
NC& operator=(NC const&) = delete;
};
But sometimes, we want to prevent copy, but would like to allow Builder (as in the Pattern).
In this case, your example works as long as RVO kicks in, which is a bit annoying as it is, in essence, non-standard. A definition of the copy constructor should be provided but you wish it not to be used.
In C++11, this usecase is supported by deleting copy operations and defining move operations (even privately).
It is allowed, but you may get a link error because the copy constructor is not implemented.
If you provide a body for NonCopyable( const NonCopyable&), it would work.
One important point may overshadow the actual question.
What is the use case of such function if RVO is allowed ?
This function can be called in 3 ways and 2 of them will be a compiler error:
NonCopyable obj;
NonCopyable obj2 = obj; // 1 --> error
NonCopyable &r = obj; // 2 --> error
const NonCopyable &rc = obj; // 3 --> ok, but dangerous (may lead to UB)
One can't use the returned object effectively, so it really doesn't matter if the RVO is allowed or not.