CWG2356:
Base class copy and move constructors brought into a derived class via a using-declaration should not be considered by overload resolution when constructing a derived class object.
But other constructors that are inherited from the base class only initializes the base class subobject, too.
So, why base class copy and move constructors should not be inherited?
Keep in mind, the default implementations of the copy/move constructors of the derived class already call the base class copy/move constructors. If the base class copy and move constructors were eligible for overload resolution, you would make the following legal, which in general is not desirable:
Base b;
Derived d = b;
Related
I have a move-only Base class and a Derived which inherits Base's constructors. I would like to give a Derived a custom destructor, but when I do so it no longer inherits Base's move constructor. Very mysterious. What is happening?
godbolt
// move-only
struct Base {
Base() = default;
Base(Base const &) = delete;
Base(Base &&) {}
};
struct Derived : public Base {
using Base::Base;
// remove this and it all works
~Derived() { /* ... */ }
};
int main() {
Base b;
// works
Base b2 = std::move(b);
Derived d;
// fails
Derived d2 = std::move(d);
}
The move constructor is not inherited with using Base::Base; in the way that you seem to think it is, because the move constructor in Base does not have the signature that a move constructor in Derived would have. The former takes a Base&&, the latter a Derived&&.
Then in Derived you are declaring a destructor. This inhibits the implicit declaration of a move constructor for Derived. So there is no move constructor in Derived.
The compiler then falls back to Derived's implicitly generated copy constructor for Derived d2 = std::move(d);. But that is defined as deleted because the base class of Derived is not copy-able. (You manually deleted Bases copy constructor.)
In overload resolution the deleted copy constructor is chosen over the Base classes inherited Base(Base&&) constructor (although a Derived rvalue could bind to Base&&), because the latter requires a conversion sequence that is not considered exact match, while binding to a const Derived& is considered exact match for the purpose of overload resolution.
Also there is the proposed wording for the resolution of CWG issue 2356 which would exclude the inherited Base move constructor from participating in overload resolution at all. (From what I can tell this is what the compiler are implementing already.)
If you don't have a good reason to declare a destructor, don't do so. If you do have a reason, then you need to default the move operations again, as you did for the move constructor in Base. (You probably want to default the move assignment operator as well if the classes are supposed to be assignable.)
If you intend to use the class hierarchy polymorphically, you should declare a virtual (defaulted) destructor in the polymorphic base, but you do not need to declare a destructor in the derived classes.
Move constructors are generated under specific circumstances.
https://en.wikipedia.org/wiki/Special_member_functions
In creating a destructor, you have stopped the generation of a move constructor by the compiler.
Also, create a virtual Base destructor if you don't have one. Default it if it doesn't have to do anything special. Same with your Base move constructor, just don't leave it empty, declare it default. You're using =delete, use =default as well.
The inherited move constructor does not have the signature for the derived class.
In the first case without the explicitly declared destructor the compiler implicitly declares the default move constructor for the derived class.
In the second case when the destructor is explicitly declared the move constructor is not implicitly declared by the compiler.
From the C++ 17 Standard (15.8.1 Copy/move constructors)
8 If the definition of a class X does not explicitly declare a move
constructor, a non-explicit one will be implicitly declared as
defaulted if and only if
(8.1) X does not have a user-declared copy constructor,
(8.2) X does not have a user-declared copy assignment operator,
—(8.3) X does not have a user-declared move assignment operator, and
> —(8.4) X does not have a user-declared destructor.
But in any case the ,move constructor of the base class is not the move constructor of the derived class due to different signatures.
I have following scenario:
I have an Abstract base class with
no copy constructor
no assignment operator
has some data members
a derived class (derived from above base class)
has assignment operator
has some data members
How the members of base class will be copied when we copy a derived class object to another existing derived class object.
I understand as we have an assignment operator defined for derived class which copies derived class members but how base class members will be copied, will it be through default assignment operator?
How the members of base class will be copied
Since there's no explicit copy constructor, one will be generated by the compiler. It will in turn call/generate copy constructors for the data members of the base class. At the end, builtin types (int, float, pointers! etc are simply copied), types with copy constructors can copy themselves, any other type will generate a compile time error.
https://en.wikipedia.org/wiki/C++11#Object_construction_improvement
For base-class constructors, C++11 allows a class to specify that base
class constructors will be inherited. Thus, the C++11 compiler will
generate code to perform the inheritance and the forwarding of the
derived class to the base class. This is an all-or-nothing feature:
either all of that base class's constructors are forwarded or none of
them are. Also, restrictions exist for multiple inheritance, such that
class constructors cannot be inherited from two classes that use
constructors with the same signature. Nor can a constructor in the
derived class exist that matches a signature in the inherited base
class.
Can someone give me an example to illustrate the issue with "Nor can a constructor in the derived class exist that matches a signature in the inherited base class."?
It means that if you have constructor in the derived class whose parameter list matches the parameter list of any constructor in the base class, then that derived class' constructor is taken and hides the base class'
E.g.
struct Foo
{
Foo(){std::cout << "Foo default ctor\n";}
Foo(int){std::cout << "Foo(int)\n";}
};
struct Bar : Foo
{
using Foo::Foo;
Bar(int){std::cout << "Bar\n";} // won't implicitly call Foo(int)
};
int main()
{
Bar b(1);
}
From §12.9/3 [class.inhctor] (Emphasis mine):
For each non-template constructor in the candidate set of inherited constructors other than a constructor
having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly
declared with the same constructor characteristics unless there is a user-declared constructor with the same
signature in the complete class where the using-declaration appears or the constructor would be a default,
copy, or move constructor for that class.
I understand that whenever a custom copy constructor or an assignment operator is defined in a derived class then it is the responsibility of those methods to call the respective methods of the base class. Now my focus is on the move constructors. Suppose the following is my move constructor. I have two ways of calling the base class constructor. Taken from here
Derived(Derived&& d):Base(d) -->Form A
{}
Derived(Derived&& d):Base(std::move(d)) -->Form B
{}
Now which method is correct. From my understanding and from the last answer on the post using Form B would be dangerous and incorrect as the object will be nullified when the derived class constructor is called.However in formA the base class copy constructor is called. Would it be better to call FormA . Similarly in the move copy assignment operator wouldn't it be better to call the base class assignment operator then the base class.
Form A is incorrect. It does not implement the move semantics. With respect to the version Form B, the statement that "d is nullified by Base(std::move(d))" is inaccurate. The accurate statement should be "the Base part (subobject) of d is nullified".
Besides ,I suggest you to explicitly cast d to the base type before invoking the base constructor. That is, Base(std::move(static_cast<Base&>(d))). This avoids potential problems if Base has a template constructor. For example, consider the case when Base is std::function. Without the explicit cast, you will end up with infinite recursion because of constructor (5) of std::function.
I have a bunch of questions related to this, I couldn't find exact answers.
Class A is the main class, and B is a subclass
If A::operator=(const A & other) was defined, does the default implementation of B::operator= copies members of B then calls A::operator= ?
If A copy constructor was defined, does the default implementation of the copy constructor for B copy-constructs members of B, then calls A copy constructor?
Do I need to define above functions virtual in A to get this behavior? (I suppose yes for operator= and no for the copy constructor, as virtual constructors is a nonsense?)
Can I forbid overloading the assignment operator or the copy constructor for subclasses of A, to force the use of default implementations?
The idea behind this, being to offer a plugin API for my users. I need the API to be C++ as scripts are too slow (I'll try JIT compilation one day), but it should be very simple.
If the default copy-assignment operator of class B is not deleted, [class.copy]/28
The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy-/move assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list [i.e. in the order in which they're listed after class X : /*here*/ {/*...*/};], and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition.
Similarly, [class.copy]/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.
The order is: first the base class(es) (base class subobjects), then the direct non-static data members, similarly to 1).
For the behaviour described in 1) and 2), no. Virtual assignment operators are hardly ever useful. Constructors may not be virtual at all (doesn't make sense).
In order for a virtual function in a derived class B to override a virtual function in a base class A, it must have the same parameter types. That is, you could have a virtual A& operator=(A const&); in the base class A, but the override in class B had to look like virtual B& operator=(A const&);, which is not a copy-assignment operator for B, because of the parameter type.
Not without "hacks". And you're not actually overloading it, but hiding all base class assignment operators. Otherwise, this would be legal:
class A {};
class B { int i; };
A a;
B b = a; // using A::operator=(A const&)