Default constructor - c++

struct Base{
Base(Base &){} // suppress default constructor
};
struct Derived : Base{
};
int main(){
Derived d;
}
The code shown gives error because the default constructor (implicit) of 'Base' is suppressed. Indeed the standard says in $12.1 "If there is no user-declared constructor for class X, a default constructor is implicitly declared."
There are three things:
a) Does the standard say anywhere that
if the user declared constructor is
present in a class, the default
constructor (implicit) is suppressed. It is bascically the above phrased negatively or is it once again implied :)?
b) Why is it that way?
c) Why the same rules do not apply for the default destructor?

I think that a) is sufficiently clearly implied by your quote.
As for “why” – quite simple: a default constructor is not always meaningful; if there were no way to suppress it, this would weaken C++ substantially.
As for c), a class without destructor (no “default”, just plain destructor) is simply not meaningful.

a) Does the standard say anywhere that
if the user declared constructor is
present in a class, the default
constructor (implicit) is suppressed.
It is bascically the above phrased
negatively or is it once again implied
:)?
Yes, that is the meaning
b) Why is it that way?
Most likely, if you have a user-defined constructor, it means special work needs to be done to initialize the object. It makes sense to disable the implicitly generated default constructor in such a case, because it likely won't do any of the special work.
c) Why the same rules do not apply for the default destructor?
Well, perhaps it would make sense for the language to enforce the "rule of three" (if you define one of copy constructor, assignment operator or destructor, chances are you need to implement all three), but it just doesn't.
Perhaps the rationale is that there are several ways to initialize a class, but assignment and destruction often works the same way (memberwise assignment, run destructor of all members).

The shortest answer is because you declared a constructor for the class Base, no default constructor is created (thus the suppression). You cannot initialize Derived because Derived has no default constructor to call on class Base. (this is because the default constructor of derived that is generated for you can only construct it's parent class with the default constructor)

Related

In c++, Does it make sense to prohibit copy construction if the default construction is prohibited in the first place?

I was going through a code implementation where the intention was to not let anyone make objects of a particular class. Here is the code snippet:
class CantInstantiate
{
CantInstantiate();
CantInstantiate(const CantInstantiate&);
...
};
Is it really required to make the copy constructor private undefined if the default constructor is already made private undefined (provided there is no other constructor)? What is the benefit of preventing copy of an object when we don't have an original object in the first place? Please explain. Thanks in advance.
If the intent is to prevent making instances of a class then you need to ensure you can't call any constructor. If you don't explicitly prohibit copy construction then you're leaving it up to the compiler to decide whether to include one or not, based on the conditions for causing a deleted implicitly-declared copy constructor which are:
- T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
- 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;
- T is a union-like class and has a variant member with non-trivial copy constructor;
- T has a data member of rvalue reference type;
- T has a user-defined move constructor or move assignment operator (this condition only causes the implicitly-declared, not the defaulted, copy constructor to be deleted).
So if we assume that you have a class A that attempts to prevent itself being constructed by only prohibiting default construction and not including anything in the list above, it's possible to exploit the implicit copy constructor to gain an instance of A, or even to inherit from it. For example:
class A
{
A(){};
};
struct B : public A {
B() : A(*a)
{}
A* a;
};
B b;
A a (*static_cast<A*>(nullptr));
Now admittedly the code above could produce unexpected behaviour and any decent compiler would produce warnings if you attempted to do it but it does compile. The practicality of such an approach would depend entirely upon what else was declared within A...
I would argue that if the idea is to stop creation then you need to ensure that all methods of construction are prevented. Therefore, it's a much better approach to explicitly delete the: default constructor, copy constructor and move constructor. This sends a much clearer statement of intent and I believe it should prevent all methods of construction.
class CantInstantiate
{
public:
CantInstantiate() = delete;
CantInstantiate(const CantInstantiate&) = delete;
CantInstantiate(CantInstantiate&&) = delete;
...
};

Warning: definition of implicit copy constructor is deprecated

I have a warning in my C++11 code that I would like to fix correctly but I don't really know how. I have created my own exception class that is derived from std::runtime_error:
class MyError : public std::runtime_error
{
public:
MyError(const std::string& str, const std::string& message)
: std::runtime_error(message),
str_(str)
{ }
virtual ~MyError()
{ }
std::string getStr() const
{
return str_;
}
private:
std::string str_;
};
When I compile that code with clang-cl using /Wall I get the following warning:
warning: definition of implicit copy constructor for 'MyError' is deprecated
because it has a user-declared destructor [-Wdeprecated]
So because I have defined a destructor in MyError no copy constructor will be generated for MyError. I don't fully understand if this will cause any issues...
Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error) has a virtual destructor.
Hence I guess it is better not to remove the virtual destructor but to define the copy constructor. But if I need to define the copy constructor maybe I should also define the copy assignment operator and the move constructor and the move assignment operator. But this seems overkill for my simple exception class!?
Any ideas how to best fix this issue?
You do not need to explicitly declare the destructor in a derived class:
§ 15.4 Destructors [class.dtor] (emphasis mine)
A destructor can be declared virtual (13.3) or pure virtual (13.4); if
any objects of that class or any derived class are created in the
program, the destructor shall be defined. If a class has a base class
with a virtual destructor, its destructor (whether user- or
implicitly-declared) is virtual.
In fact, it might even be detrimental to performance in some cases, as explicitly declaring a destructor will prevent the implicit generation of a move constructor and move assignment operator.
Unless you need to do something in your destructor, the best course of action would be to just omit an explicit declaration of a destructor.
If you do need a custom destructor, and are certain that the default copy ctor, copy assignment operator, move ctor and move assignment operator would do the correct thing for you, it is best to explicitly default them like so:
MyError(const MyError&) = default;
MyError(MyError&&) = default;
MyError& operator=(const MyError&) = default;
MyError& operator=(MyError&&) = default;
Some reasoning on why you're seeing the error, because this used to be perfeclty valid code in C++98:
As of C++11, implicit generation of the copy constructor is declared as deprecated.
§ D.2 Implicit declaration of copy functions [depr.impldec]
The implicit definition of a copy constructor as defaulted is
deprecated if the class has a user-declared copy assignment operator
or a user-declared destructor. The implicit definition of a copy
assignment operator as defaulted is deprecated if the class has a
user-declared copy constructor or a user-declared destructor (15.4,
15.8). In a future revision of this International Standard, these implicit definitions could become deleted (11.4).
The rationale behind this text is the well-known Rule of three.
All quotes below are sourced from cppreference.com: https://en.cppreference.com/w/cpp/language/rule_of_three
Rule of Three
If a class requires a user-defined destructor, a user-defined copy
constructor, or a user-defined copy assignment operator, it almost
certainly requires all three.
The reason why this rule of thumb exists is because the default generated dtor, copy ctor and assignment operator for handling different types of resources (most notably pointers to memory, but also others, like file descriptors and network sockets to name just a couple) rarely do the correct behaviour. If the programmer thought that he needed special handling for the closing of a file handle in the class destructor, he most surely wants to define how this class should be copied or moved.
For completeness, below are the often related Rule of 5, and the somewhat disputed Rule of Zero
Rule of Five
Because the presence of a user-defined destructor, copy-constructor,
or copy-assignment operator prevents implicit definition of the move
constructor and the move assignment operator, any class for which move
semantics are desirable, has to declare all five special member
functions:
Rule of Zero
Classes that have custom destructors, copy/move constructors or
copy/move assignment operators should deal exclusively with ownership
(which follows from the Single Responsibility Principle). Other
classes should not have custom destructors, copy/move constructors or
copy/move assignment operators.
Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error) has a virtual destructor.
You thought wrong. Derived classes will always have virtual destructor if you define one in base, no matter if you create it explicitly or not. So removing destructor would be simplest solution. As you can see in documentation for std::runtime_exception it does not provide it's own destructor either and it is compiler generated because base class std::exception does have virtual dtor.
But in case you do need destructor you can explicitly add compiler generated copy ctor:
MyError( const MyError & ) = default;
or prohibit it making class not copyable:
MyError( const MyError & ) = delete;
the same for assignment operator.
Note: the same happens for much different code but I'm writing it here in case someone gets the same warning.
There was a bug in GCC versions 6.4 - 9.0 where using declarations for base_type's operator= and base_type's ctor in types inherited from base_type which was a class template did not actually create copy/move ctor/operators (ending in very unexpected compiler errors that an object can not be copied/moved).
Since GCC 9.0, the bug is fixed but it creates this warning instead. The warning is wrong and should not appear (the using explicitly declares constructors/operators).
Example code with workarounds and GCC version comparison: https://godbolt.org/z/WgIH4c
GCC bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89381
another GCC bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92145
Origin of this discovery: https://github.com/boostorg/spirit/issues/465
This will make your code compile (but not work) in clang 13:
MyError(const MyError&) {};
MyError(MyError&&) {};
MyError& operator=(const MyError&) {};
MyError& operator=(MyError&&) {};
You will need to fill in the appropriate code for the copy constructor,
but also be aware that you don't need all 4 functions, only the ones that are being called.

Inheritance and Compiler-Generated functions

When i have inheritance, does the compiler-generated functions that i usually get (constructor, destructor, assignment operator and copy constructor) are still generated for my classes?
Let's say i have this inheritance: A base class, B which inherits A (public) and C which public inherits B.
My A class has no memory allocation or anything that requires a destructor to be implemented by me, and i'm not implementing a destructor there, when i compile my program will it still create an empty A::~A(){} ?
Same for B and C.. Thank you!
The rule of 5 still applies to each of the classes, independent of their inheritence.
In other words, if B is derived from A, just because A defined their copy constructor, that doesn't affect the generation of Bs copy constructor.
You should, however, be mindful to define a virtual destructor for the base class if needed.
Yes, of course. And the constructor/destructor chained calls are still present (i.e., C destructor will call B destructor which calls A destructor, same in the reverse order for constructors).
Yes, Compiler always generates (constructor, destructor, assignment operator and copy constructor) for the classes , where user have not defined these functions explicitly.
Compiler inserts constructor, destructor,copy constructor and overloaded assignment operator in a class if it is not defined by the user.
But the most important thing is if user defines a parameterized constructor in a class,then compiler will not generate the default constructor and and object creation without any parameter will throw a linker error.
Say for example you have a class A
class A
{
int a;
public:
//.....
//some line of code
//.....
}
If you don't provide any constructor, the compiler will generate a default constructor which does not takes any parameter A(){}.
But if by any chance you declare a parametrized constructor like
A(int i)
{
a = i;
}
The compiler doesn't generate any default constructor and your object creation with no parameter will fail.
A a; ---> This will fail.
A b(10) ---> This will pass.
So thumb of rule is, if your are providing a constructor of your own, always provide the default constructor along with it.

No implicit copy constructor in polymorphic class?

In C++11, a polymorphic class (one with virtual member methods) should/must have a virtual destructor (so that delete on a base-class pointer does the expected). However, declaring an destructor explicitly deprecates the implicit generation of the copy constructor (though this may not be widely implemented by compilers) and hence also of the default constructor. Thus, for any polymorphic class to not be deprecated it must have these members
virtual ~polymorphic_class() = default;
polymorphic_class() = default;
polymorphic_class(polymorphic_class const&) = default;
explicitly defined, even if they are trivial. Am I correct? (Isn't this annoying?) What is the logic behind this? Is there any way to avoid that?
Am I correct?
Yes, as per ForEveR's post.
Is there any way to avoid that?
Yes. Do this just once by implementing a base for all polymorphic classes (similarly to class Object in Java and D which is the root of all class hierarchies):
struct polymorphic {
polymorphic() = default;
virtual ~polymorphic() = default;
polymorphic(const polymorphic&) = default;
polymorphic& operator =(const polymorphic&) = default;
// Those are not required but they don't harm and are nice for documentation
polymorphic(polymorphic&&) = default;
polymorphic& operator =(polymorphic&&) = default;
};
Then, any class publicly derived from polymorphic will have a implicitly declared and defined (as defaulted) virtual destructor unless you declare one yourself.
class my_polymorphic_class : public polymorphic {
};
static_assert(std::is_default_constructible<my_polymorphic_class>::value, "");
static_assert(std::is_copy_constructible <my_polymorphic_class>::value, "");
static_assert(std::is_copy_assignable <my_polymorphic_class>::value, "");
static_assert(std::is_move_constructible <my_polymorphic_class>::value, "");
static_assert(std::is_move_assignable <my_polymorphic_class>::value, "");
What is the logic behind this?
I can't be sure. What follows are just speculations.
In C++98/03, the Rule of Three says that if a class needs either the copy constructor, the copy assignment operator or the destructor to be user defined, then it probably needs the three to be user defined.
Obeying the Rule of Three is a good practice but this is nothing more than a guideline. The Standard doens't force it. Why not? My guess is that people realized this rule only after the publication of the Standard.
C++11 introduced move constructor and move assignment operator, turning the Rule of Three into the Rule of Five. With benefit of hindsight, the committee wanted to enforce the Rule of Five. The idea was: if either of the five special functions is user declared then the others, but the destructor, won't be implicitly defaulted.
However, the committee didn't want to break virtually every C++98/03 code by enforcing this rule and then, decided to do it only partially:
If either the move constructor or the move assignment operator is user declared then other special functions, but the destructor, will be deleted.
If either of the five special functions is user declared then the move constructor and move assignment operators won't be implicitly declared.
In the case of C++98/03 well formed code, neither a move constructor nor a move assignment operator is ever user declared then, rule 1 doesn't apply. Hence when compiled with a C++11 compliant compiler C++98/03 well formed code doesn't fail to compile as a consequence of this rule. (If it does, it's for some other reasons.)
In addition, under rule 2 the move constructor and move assignment operator are not implicitly declared. This doesn't break C++98/03 well formed code either because they never expected the declaration of move operations anyway.
The deprecation mentioned in the OP and quoted in ForEveR's post suggests a possible enforcement of the Rule of Five by a future Standard. Be prepared!
You are correct, that should be true by standard in future, but now it's only deprecated, so every compiler should support implicitly-declared copy constructor, when destructor is virtual now.
n3376 12.8/7
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class
definition declares a move constructor or move assignment operator, the implicitly declared copy constructor
is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has
a user-declared copy assignment operator or a user-declared destructor.
And it seems to me, that you cannot make any workaround for this.

c++ syntax: default and delete modifiers

Today I stumbled over a code snippet like this one:
class A
{
A() = default;
A (const A&) = delete;
...
}
I've never seen either the delete or default keyword. Are they part of C++11 std? And what are they used for?
Special member functions can now be defaulted or deleted.
A deleted member function still takes part in overload resolution, but if it gets chosen, the program is ill-formed and compilation stops with a useful diagnostic. This is The Right Way to write things like non-copyable classes, and the user gets a proper error message.
A defaulted member function "does what it should", e.g. a defaulted default constructor default-initializes all bases and members and has empty body; a defaulted copy constructor copies each base and member object, and a defaulted assignment operator assigns each base and member object. If any of those operations aren't allowed (e.g. you have reference members), then the defaulted member function is defined as deleted.
Note that your first declaration-definition A() = default; makes the constructor A::A() user-declared but not user-defined; this is important for the classification of A, e.g. whether it is POD. (And notice that this is different from struct A { A(); }; A::A() = default; which is user-defined.)
Another nice consequence is the clarification of implicitly generated things: If you don't write certain functions yourself at all (like copy constructors), one gets implicitly declared for you. When the implicitly-declared one is odr-used, it gets implicitly defined as defaulted, and thus if it's not possible (e.g. if the class has non-copyable members), it actually gets implicitly defined as deleted. So that's generally a neat way of propagating things like non-copyability and non-assignability, at least in terms of the language and the consequent diagnostics.