Unreasonably deleted move constructor - c++

I have a class with explicitely deleted copy constructor named, let's say, NonCopyable. Then there's a class Base1 with a member of type NonCopyable. And another class Base2 with Base1 as parent. And finally - a Derivative class which parent is Base2.
Since NonCopyable is non-copyable, it's obvious that Base1, Base2 and Derivative will be also non-copyable. But it seems like there's been also move constructor (and assign operator) deleted.
Test it here:
GCC
MSVC
Following line gives those errors:
Derived d1, d2 = std::move(d1);
GCC: 'Derived::Derived(Derived&&)' is implicitly deleted because the default definition would be ill-formed:
class Derived :
the rest of errors just claims that Base1 and Base2 copy ctors are implicitly deleted which is not weird
MSVC: error C2280: 'Derived::Derived(const Derived &)': attempting to reference a deleted function
In provided links there's also commented lines which gives similar errors. Please uncomment them to see more errors I'd like to show you (e.g. with remove_if uncommented it complains about deleted move (on GCC) /copy (on MSVC) assign operator).
What I want to achieve is make Derivative (and its bases also) moveable, but not copyable.
I'm working with Visual Studio 2015 and getting exactly the same error as in the msvc link I provided, but I'm not sure what version of MSVC is used to compile on rextester.com.
I'm pasting the code also here at the request of #Richard Critten:
class NonCopyable
{
public:
NonCopyable() { }
NonCopyable(const NonCopyable &) = delete;
NonCopyable & operator=(const NonCopyable &) = delete;
NonCopyable(NonCopyable &&) { }
NonCopyable & operator=(NonCopyable &&) { return *this; }
};
class Base1
{
public:
virtual ~Base1() = default;
private:
NonCopyable m;
};
class Base2 :
public Base1
{
public:
virtual ~Base2() = default;
};
class Derived :
public Base2
{
};
int main()
{
std::vector<Derived> v;
//std::remove_if(v.begin(), v.end(), [](const Derived &) { return true; });
//v.emplace_back();
Derived d1, d2 = std::move(d1);
}

[class.copy]/9 If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
(9.4) — X does not have a user-declared destructor.
Base1 has a user-declared destructor, so no move constructor for it. And copy constructor is implicitly declared as deleted due to non-copyable member. So Base1 can be neither copied nor moved, and of course Base2 and Derived together with it.

Related

Would a derived class ever have an implicit copy constructor or assignment operator when it's deleted in the base class?

Qt defines Q_DISABLE_COPY as follows:
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
Q_DISABLE_COPY is used in the QObject class, but the documentation for it says that it should be used in all of its subclasses as well:
when you create your own subclass of QObject (director or indirect), you should not give it a copy constructor or an assignment operator. However, it may not enough to simply omit them from your class, because, if you mistakenly write some code that requires a copy constructor or an assignment operator (it's easy to do), your compiler will thoughtfully create it for you. You must do more.
But consider this program:
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1); // error: call to implicitly-deleted copy constructor of 'Derived'
Derived d3;
d3 = d1; // error: object of type 'Derived' cannot be assigned because its copy assignment operator is implicitly deleted
}
The errors from trying to compile that program seem to indicate that the compiler will not create copy constructors or assignment operators in derived classes when they're deleted in base classes. Is Qt's documentation just wrong about this, or is there some edge case when it would create them?
Related, but not a duplicate: Repeating Q_DISABLE_COPY in QObject derived classes. It gives reasons why it may be useful to use Q_DISABLE_COPY in a class even if it wouldn't be copyable anyway, but doesn't confirm that it never will in fact be copyable without it.
Since the base class copy constructor is deleted, the derived class has no way to know how to copy the base class object. This will disable any implicit copy constructors provided by the compiler.
From cppreference:
The implicitly-declared or defaulted copy constructor for class T is
defined as deleted if any of the following conditions are true:
T has direct
or virtual base class that cannot be copied (has deleted,
inaccessible, or ambiguous copy constructors)
T has direct or virtual
base class with a deleted or inaccessible destructor;
Inheriting Q_DISABLE_COPY can be useful when the user inherits from a class that deletes the default copy constructor, but provides their default implementation to override it.
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {
Derived() = default;
Derived(const Derived&) : Derived() {}
Derived &operator=(const Derived&) {
return *this;
}
};
struct MoreDerived : Derived {};
int main() {
Derived d1;
Derived d2(d1); // Works fine!
Derived d3;
d3 = d1; // Works fine!
MoreDerived md1;
MoreDerived md2(md1); // Works fine!
MoreDerived md3;
md3 = md1; // Works fine!!
}
Edit: As #SR_ rightly states, in the above implementation of Derived, Base is not being copy constructed. I just wanted to illustrate the fact that it is easy to introduce an unintentional copy constructor when another class is modified in the inheritance hierarchy.
Prior to commit a2b38f6, QT_DISABLE_COPY was instead defined like this (credit to Swift - Friday Pie for pointing this out in a comment):
#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
And Q_DECL_EQ_DELETE like this:
#ifdef Q_COMPILER_DELETE_MEMBERS
# define Q_DECL_EQ_DELETE = delete
#else
# define Q_DECL_EQ_DELETE
#endif
Q_COMPILER_DELETE_MEMBERS got defined if C++11 support (or at least a new enough draft of it to support = delete) was available.
Thus, if you compiled Qt back then against a C++03 compiler, it would instead have compiled something like this:
struct Base {
Base() {};
private:
Base(const Base &);
Base &operator=(const Base &);
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1);
Derived d3;
d3 = d1;
}
And compiling that with g++ -std=c++03 gives you these errors:
<source>: In copy constructor 'Derived::Derived(const Derived&)':
<source>:9:8: error: 'Base::Base(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:5:5: note: declared private here
5 | Base(const Base &);
| ^~~~
<source>: In function 'int main()':
<source>:13:18: note: synthesized method 'Derived::Derived(const Derived&)' first required here
13 | Derived d2(d1);
| ^
<source>: In member function 'Derived& Derived::operator=(const Derived&)':
<source>:9:8: error: 'Base& Base::operator=(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:6:11: note: declared private here
6 | Base &operator=(const Base &);
| ^~~~~~~~
<source>: In function 'int main()':
<source>:15:10: note: synthesized method 'Derived& Derived::operator=(const Derived&)' first required here
15 | d3 = d1;
| ^~
So back then, "your compiler will thoughtfully create it for you" was technically true but not practically so, since the compiler creating it would cause compilation to fail, just with a different (and arguably less clear) error. I'm now convinced that it's not true at all anymore now that = delete is unconditionally used, so I plan to ask Qt's maintainers to remove/reword that section of their documentation.

Is deleting copy and move constructors/assignment operators in base class enough?

If I have an abstract base class and I want to make all derived classes noncopyable and nonmovable is it sufficient to declare these special member functions deleted in the base class? I want to ensure that my entire class hierarchy is noncopyable and nonmovable and am wondering if I can get away with not having to declare those 4 special member functions as deleted in every derived class. I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class but the following example results in a compilation error when I try to define a defaulted copy assignment operator so I'm unsure. This is the error:
derived_class.cc:15:15: error: defaulting this copy constructor would delete it after its first declaration
DerivedClass::DerivedClass(const DerivedClass &) = default;
derived_class.h:9:22: note: copy constructor of 'DerivedClass' is implicitly deleted because base class 'virtual_functions::BaseClass' has a deleted copy constructor
class DerivedClass : public BaseClass {
base_class.h:11:3: note: 'BaseClass' has been explicitly marked deleted here
BaseClass(const BaseClass &) = delete;
// base_class.h
class BaseClass {
public:
BaseClass(const BaseClass &) = delete;
BaseClass(BaseClass &&) = delete;
BaseClass &operator=(const BaseClass &) = delete;
BaseClass &operator=(BaseClass &&) = delete;
virtual ~BaseClass() = default;
virtual bool doSomething() = 0;
protected:
BaseClass(std::string name);
private:
std::string name_;
};
// derived_class.h
class DerivedClass : public BaseClass {
public:
DerivedClass();
DerivedClass(const DerivedClass &);
bool doSomething() override;
};
// derived_class.cc
DerivedClass::DerivedClass(const DerivedClass &) = default;
You cannot prevent a child class from defining its own copy/move constructor. That said, it will prevent it "out of the box", meaning if you do not provide one, or use a inline default constructor, it will also be marked as deleted. The reason you get a error here when you try to just define the constructor as default is because you are not allowed to do that in an out of line definition when a member or base has implicitly deleted it. Had you used
class DerivedClass : public BaseClass {
public:
DerivedClass(const DerivedClass &) = default;
bool doSomething() override;
};
then the code would compile, and you would only get an error if you actually try to call the copy constructor. This works because an inline implicit default is allowed even when a member or base implicitly deletes it and the end result is the constructor is implicitly deleted.
Is deleting copy and move constructors/assignment operators in base class enough?
It is enough to prevent implicitly generated copy and move constructors/ assignment operators.
I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class
This is correct. You cannot prevent this. Well, you can prevent this by declaring the class final. Then there cannot be derived classes, and thus derived classes cannot be copyable.
Of course, such explicitly declared copy constructor (and other) will not be able to copy the base sub object that is non-copyable. The constructors must use BaseClass(std::string) and the assignment operators cannot modify the state of the base object in any way (unless they use some trick to get around access specifier encapsulation).
You cannot prevent a derived class to declare copy/move constructors, but they cannot be defaulted: the default copy ctor or a derived class would try to call the copy ctor of its base (same for move).
But the derived class can explicitely construct its base with its default ctor:
class DerivedClass : public BaseClass {
public:
DerivedClass();
DerivedClass(const DerivedClass &): BaseClass() {
// copy ctor for the derived part
}
bool doSomething() override;
};
Et voila... the class DerivedClass is now copyable despite its base class is not!

Why does gcc warn about calling a non-trivial move assignment operator with std::tuple and virtual inheritance?

In the following example gcc 7 gives a warning:
defaulted move assignment for 'B' calls a non-trivial move assignment
operator for virtual base 'A' [-Wvirtual-move-assign]
if I create an std::tuple<B> object. Clang 5 doesn't report any problems. Also the problem goes away if vector is removed from Base. Example.
#include <tuple>
#include <vector>
class Base
{
public:
virtual ~Base();
std::vector<int> v;
};
class A : public Base
{
};
class B : public virtual A
{
};
int main()
{
B *b = new B;
B another{*b}; // <<<--- this compiles
std::tuple<B> t; // <<<--- gives warning
}
Why does it happen in presence of std::tuple (and absence of move assignment) and what is the proper way to fix it if I need to keep such a hierarchy?
The warning is unrelated to tuple, it is triggered by a move assignment of B. For instance, the following code generates the warning
B b;
B t;
t = std::move(b);
The gcc documentation explains the intent of the warning
-Wno-virtual-move-assign
Suppress warnings about inheriting from a virtual base with a non-trivial C++11 move assignment operator. This is dangerous because if the virtual base is reachable along more than one path, it is moved multiple times, which can mean both objects end up in the moved-from state. If the move assignment operator is written to avoid moving from a moved-from object, this warning can be disabled.
Here's an example that demonstrates the problem
struct Base
{
Base() = default;
Base& operator=(Base&&)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
struct A : virtual Base {};
struct B : virtual Base {};
struct C : A, B {};
int main()
{
C c;
c = C();
}
This produces the output
Base& Base::operator=(Base&&)
Base& Base::operator=(Base&&)
showing that the single Base instance was moved assigned to twice, once by each move assignment operator of A and B. The second move assignment is from an already moved from object, which could cause the contents from the first move assignment to be overwritten.
Note that clang also generates a warning in my example, it's probably detecting that A is not reachable via two paths in your example and not emitting the warning.
The way to fix it is to implement move assignment operators for A and B that can detect that Base has been moved from and omit a second move assignment.

C++ non-static data member initializers, just a little bit confused

I am a little bit confused about why the following code does what it does:
class Base
{
public:
Base() = default;
Base(const Base &) =delete;
Base &operator=(const Base &) = delete;
Base(const char*) {}
};
class Holder
{
public:
Holder() = default;
private:
// Base b = Base();
Base b2 = {};
};
int main()
{
Holder h;
}
in this incarnation, it compiles, however if I un-comment Base b = Base(); it gives the following error:
main.cpp:15:17: error: use of deleted function 'Base::Base(const Base&)'
Base b = Base();
^
main.cpp:5:6: note: declared here
Base(const Base &) =delete;
^
and I am just unable to find in the standard why it tries to call the copy constructor for the Base b = Base() initializer, and why doesn't it call for the Base b2 = {} ... or is this just one of those little obscurities that is hidden in a few words in a paragraph somewhere?
Can you please give a (short) explanation why this happens?
(coliru: http://coliru.stacked-crooked.com/a/c02ba0293eab2ce5 )
That's because, conceptually, that line constructs from Base(), which requires a copy/move constructor. The probable reason why you weren't aware of this, is because that expression generally triggers copy elision: a standard optimization. It's one of those C++ gotcha's.
(31.3) — when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move.
As for why Base b2 = {} works, see
(3.4) — Otherwise, if the initializer list has no elements and T is a
class type with a default constructor, the object is
value-initialized.
You could just do Base b;.
T object = {arg1, arg2, ...}; is syntax for list initialization. There is no copying involved.
T object = T() is not list initialization. The right hand operand constructs a value-initialized temporary, and object is move- or copy-initialized from it. The move and copy can be elided, but the type must be movable or copyable, otherwise this is not allowed.
The true reason is because you forget keyword "explicit", you should change the code
Base(const Base &) =delete;
To:
explicit Base(const Base &) = delete;
Let me explain the reason, In class "Holder", Base b = Base() will:
Base() will generate a temporary object by calling constructor "Base()"
However, constructor "Base(const Base&)" has a single parameter and without keyword "explicit", this will make code:
Base obj = value;
become
Base obj(value);
Therefore, just add keyword "explicit" will solve this problem. ^_^

Prohibiting definition of a copy constructor in an inherited class

I want to make an abstract base class non-copyable and force any classes that derive from it to be non-copyable. The below code uses Boost's noncopyable as defined in noncopyable.hpp yet still allows D, the derived class, to define a copy constructor.
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
};
class D : noncopyable
{
public:
D() { }
D(const D&) { }
};
int main()
{
D a;
D b(a);
return 0;
}
This code compiles and runs (http://ideone.com/g4gGLm), when I expected it to throw a compile-time error about D's copy constructor. Maybe I have misinterpreted what this noncopyable class is meant to do. If so, is there any way to force derived classes to not define a copy constructor? (Answer can use C++11, but preferably not boost)
The reason this works is because D(const D&) calls the default constructor of the base class, not the copy constructor. (counter-intuitive at first, but it makes sense considering all constructors behave like this)
Since the copy constructor isn't called, a copy of the base object isn't created unless you explicitly ask for one:
D(const D& d) : noncopyable(d) { }
which would indeed result in an error. So in fact, your issue is a non-issue - there's no copying of noncopyable going on.
I'm not aware of any straight-forward way to force a derived class do disallow copying, nor would I recommend using one if there was.
You need to delete the copy constructor of D. Right now you allow copy-construction of D's by not trying to copy-construct the base class. The following variants will not compile:
class E: noncopyable
{
};
E e, e2(e);
class F: noncopyable
{
public:
F(const F &init): noncopyable(init)
{}
};