Initializing abstract class with deleted copy constructor - c++

I have an abstract class with deleted copy constructor and copy assignment operator that is meant to be used as a public interface:
struct connection {
// Make object non copyable.
connection(const connection &) = delete;
auto operator=(const connection &) -> connection & = delete;
// Make class abstract.
virtual ~connection() = 0;
};
I'm trying to create a class that inherits from it:
struct abstract_connection : connection {...};
but I get the following error in the constructor:
constructor for 'abstract_connection' must explicitly initialize the base class 'connection' which does not have a default constructor
Why does this happen when I delete the copy constructor and operator?

According to the standard:
[class.default.ctor#1]
[...] If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted ([dcl.fct.def]). [..]
Since you have user-declared the copy-constructor by deleting it, you need to user-provide the default constructor:
struct connection {
connection(const connection &) = delete;
auto operator=(const connection &) -> connection & = delete;
virtual ~connection() = 0;
protected: // You likely want to make it protected.
connection() = default;
};

The rules behind the automatic generation of special member functions are clearly explained in Effective Modern C++ by Scott Meyers, Item 17, p.109.
In a nutshell, whenever you redefine/override a constructor, then you need to define all the needed constructors. Since you have deleted the copy constructor, you need to define the default constructor of the base class. This is becaue you are using the default constructor when you define your derived class.
This code struct abstract_connection : connection {...}; (without any given information) means that you are most likely to initialise the abstract_connection using the default ctor from connection. But the default ctor from connection is not defined.
Furthermore, you need to define your dtor, even thought it is a virtual function. The code below compiles and runs here.
struct connection
{
connection() = default;
connection(const connection &) = delete;
auto operator=(const connection &) -> connection & = delete;
virtual ~connection() = 0;
};
connection::~connection()
{}
struct abstract_connection : connection
{
abstract_connection() : connection()
{}
~abstract_connection() = default;
};
int main()
{
abstract_connection foo;
}

You defined the copy constructor as yourself (=delete). So Compiler saw that and it doesn't generate the default constructor. So in this case, when define new class abstract_connection that inherited from the base class connection, compiler doesn't know how to do constructor for new derived class abstract_connection. So It reported that error.

Related

Move constructor not inherited nor default generated

I tried extending std::ifstream with one function to make it easier to read binary variables, and to my surprise, with using std::ifstream::ifstream; the move constructor is not inherited. Worse yet, it is explicitly deleted.
#include <fstream>
class BinFile: public std::ifstream
{
public:
using std::ifstream::ifstream;
//BinFile(BinFile&&) = default; // <- compilation warning: Explicitly defaulted move constructor is implicitly deleted
template<typename T>
bool read_binary(T* var, std::streamsize nmemb = 1)
{
const std::streamsize count = nmemb * sizeof *var;
read(reinterpret_cast<char*>(var), count);
return gcount() == count;
}
};
auto f()
{
std::ifstream ret("some file"); // Works!
//BinFile ret("some file"); // <- compilation error: Call to implicitly-deleted copy constructor of 'BinFile'
return ret;
}
I don't want to explicitly implement the move constructor because it just feels wrong. Questions:
Why it is deleted?
Does it makes sense for it to be deleted?
Is there a way to fix my class so that the move constructor is properly inherited?
The issue is that basic_istream (a base of basic_ifstream, of which template ifstream is an instantiation) virtually inherits from basic_ios, and basic_ios has a deleted move constructor (in addition to a protected default constructor).
(The reason for virtual inheritance is that there is a diamond in the inheritance tree of fstream, which inherits from ifstream and ofstream.)
It's a little known and/or easily forgotten fact that the most derived class constructor calls its (inherited) virtual base constructors directly, and if it does not do so explicitly in the base-or-member-init-list then the virtual base's default constructor will be called. However (and this is even more obscure), for a copy/move constructor implicitly defined or declared as defaulted, the virtual base class constructor selected is not the default constructor but is the corresponding copy/move constructor; if this is deleted or inaccessible the most derived class copy/move constructor will be defined as deleted.
Here's an example (that works as far back as C++98):
struct B { B(); B(int); private: B(B const&); };
struct C : virtual B { C(C const&) : B(42) {} };
struct D : C {
// D(D const& d) : C(d) {}
};
D f(D const& d) { return d; } // fails
(Here B corresponds to basic_ios, C to ifstream and D to your BinFile; basic_istream is unnecessary for the demonstration.)
If the hand-rolled copy constructor of D is uncommented, the program will compile but it will call B::B(), not B::B(int). This is one reason why it is a bad idea to inherit from classes that have not explicitly given you permission to do so; you may not be calling the same virtual base constructor that would be called by the constructor of the class you are inheriting from if that constructor were called as a most-derived class constructor.
As to what you can do, I believe that a hand-written move constructor should work, since in both libstdc++ and libcxx the move constructor of basic_ifstream does not call a non-default constructor of basic_ios (there is one, from a basic_streambuf pointer), but instead initializes it in the constructor body (it looks like this is what [ifstream.cons]/4 is saying). It would be worth reading Extending the C++ Standard Library by inheritance? for other potential gotchas.
As the previous answer mentioned the constructor is defined as deleted in the base class.
It means, you can't use it - see <istream>:
__CLR_OR_THIS_CALL basic_istream(const basic_istream&) = delete;
basic_istream& __CLR_OR_THIS_CALL operator=(const basic_istream&) = delete;
And the return ret tries to use the deleted copy constructor and not the move constructor.
However if you crate your own move constructor it should work:
BinFile(BinFile&& other) : std::ifstream(std::move(other))
{
}
You can see this in one of the comments of your question (#igor-tandetnik).

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!

Unreasonably deleted move constructor

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.

Overloading copy constructor with delete and then calling the default constructor of a subclass in C++

I am trying to disable or rather delete the copy constructor of the parent class Card with the line Card(const Card&) = delete;
When I call Quartz* qu = new Quartz(); in the main i get the error that the default constructor is deleted? I find this confusing since I did not think I was defining a default constructor in Card but rather an overload of the copy constructor. Any explanations or workarounds for this much appreciated.
class Card {
public:
Card(const Card&) = delete;
};
class Quartz : public Card {
public:
Quartz() = default;
};
int main() {
Quartz* qu = new Quartz();
}
Default constructor is only implicitly defined if the class has no other constructors. Since you defined a copy constructor, you now need to explicitly define default one, too.

Code unexpectedly fails to compile. Why?

Take a look a the following code example which uses class uncopiable similar to boost::noncopyable:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) = default;
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {}
Since I wanted to make inner class move only I explicitly specified its move constructor and assignment operator to be default ones but also since I've heard that it's a good practice to specify all of the "special member functions" in such case I inherited it from uncopiable. The problem is that compilation fails with every compiler and something similar to the following error message is displayed (this message is excerpt from the clang one):
/usr/include/c++/v1/memory:1645:31: error: call to implicitly-deleted copy constructor of 'A::B'
...
main.cpp:26:10: note: in instantiation of function template specialization 'std::__1::vector >::emplace_back<>' requested here
main.cpp:19:3: note: copy constructor is implicitly deleted because 'B' has a user-declared move constructor
It could be fixed by removing inheritance (copy operations would still not be created). But writing copy operations to be explicitly deleted inside class after that is also okay.
My questions are: why does it happen? Could it be considered a deficiency of disabling constructors/assignment operators through inheritance of helper classes?
The problem is that your uncopiable class is not moveable. Therefore the default move constructor / assignment operator of the derived class try to use the deleted copy versions.
static_assert(std::is_move_constructible<uncopiable>::value, ""); // fails
static_assert(std::is_move_assignable<uncopiable>::value, ""); // fails
The reason for this is § 12.8 ¶ 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
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator, and
X does not have a user-declared destructor.
Declaring a copy operator or assignment operator as deleted still counts as declaring it.
The solution is of course to declare the move operations for uncopiable.
uncopiable(uncopiable&&) noexcept = default;
uncopiable& operator=(uncopiable&&) noexcept = default;
Note that the move operations should usually be declared noexcept. Especially if you want to use the type in a std::vector like in your example.
This compiles ok on MinGw:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) {};
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {
A* a = new A();
}