Can you tell me what is wrong in the following example? I am using C++17, where I thought the following should be supported.
class Base {
public:
virtual ~Base() = default;
};
struct Derived : public Base {
int m1;
};
int main() {
/* Results in a compilation error
* error C2440: 'initializing': cannot convert from 'initializer list' to 'Derived'
* message : No constructor could take the source type, or constructor overload resolution was ambiguous */
Derived d{ {},1 };
return 0;
}
It does not work because Derived is not an aggregate since it has a base class with virtual members.
The code compiles and runs when removing the virtual destructor in Base and using C++17.
If Base requires a virtual destructor, Derived can implement a custom constructor allowing initialization using curly brackets.
class Base {
public:
virtual ~Base() = default;
};
struct Derived : public Base {
Derived(int m): m1(m) {}
int m1;
};
int main() {
Derived d{ 1 };
return 0;
}
Related
I know it's OK to call base class function in a derived class constructor, because base class is constructed before derived class.But I'm not sure if this is a good practice.Example code is:
class Base {
public:
int Get() const { return i_; }
void Set(const int i) { i_ = i; }
private:
int i_{0};
};
class Derived : public Base {
// initialize `derived_i_` with a call to base class function, Is this a good
// practice in production code?
Derived() : derived_i_{Get()} {
// do some other things
}
private:
int derived_i_{0};
};
To be more pedantic, you could write your constructor as the following:
Derived() : Base(), derived_i_{Get()} {
// do some other things
}
The compiler should fully construct the base class before doing any initialization of the derived class.
I am trying to achieve the following design, which is a dreaded diamond situation:
struct super_base
{
super_base(int a) { b = a; }
int b;
};
struct base : virtual super_base
{};
struct other_base : virtual super_base
{};
struct derived : base, other_base
{
derived(int a) : super_base{a} {}
};
int main() {}
Which doesn't work. The error for the above code using Clang is quite
good at explaining the mistake:
error: call to implicitly-deleted default constructor of 'base'
derived(int a) : super_base{a} {}
^
note: default constructor of 'base' is implicitly deleted because base
class 'super_base' has no default constructor
So I added an empty constructor for super_base, and it works:
#include<iostream>
#include<stdexcept>
struct super_base
{
super_base() { throw std::logic_error{"Constructor should not be called."}; };
super_base(int a) { b = a; }
int b;
};
struct base : virtual super_base
{};
struct other_base : virtual super_base
{};
struct derived : base, other_base
{
derived(int a) : super_base{a} {}
};
int main() { std::cout << derived(10).b << '\n'; }
But why does this not throw ?
P.S.: I purposefully used a dreaded diamond pattern to illustrate the use of virtual inheritance. The problem remains the same with single inheritance.
P.P.S.: The compiler used is Clang 3.9.1. The results are the same with
GCC 6.3.1.
struct base:super_base {}:
this tries to create some constructors. One of them it tries to create is base::base().
If super_base has no super_base::super_base(), this constructor is deleted.
If we have super_base::super_base()=default or {}, then base::base() by default exists, even if you don't =default it.
The same thing happens in other_base.
And your derived class tries to call the base object constructors, which are deleted, which gives you an error.
Now, why isn't it called? When you have a virtual base class, the constructor is only called once. Intermediate types which call the virtual base classes constructor have their calls ignored.
So we have derived() calling base(), base() calling super_base(), but that call is ignored because of virtual inheritance.
derived()'d call of super_base(int) is used instead.
Now, why are these the rules? Because C++ doesn't have the concept of "constructor that can only be called if you are a derived class of this class explicitly calling a specific base constructor". Virtual inheritance is not quite as full featured as you might like.
Consider following program: (See live demo here.)
#include <iostream>
class Base
{
int s{9};
public:
operator int()
{
return s;
}
};
class Derived : public Base
{
int s{18};
};
int main()
{
Base b;
int s=b;
std::cout<<s<<'\n';
Derived d;
int m=d;
std::cout<<m;
}
Output of program is:
9
9
Here, the conversion operator of Base class is inherited, so the initialization of m variable is valid.
But now I want to print the value of s data member that belongs to Derived. How can I do this?
Is it necessary to rewrite conversion operator for derived class also? Can't I reuse the same Base class conversion operator?
Your s parameter of Base is shadowed in Derived, but since conversion operator in in Base class, Base::s is used inside it.
You can do this:
class Base
{
int s{9};
public:
Base() {}
Base(int v) : s(v) {}
operator int()
{
return s;
}
};
class Derived : public Base
{
public:
Derived() : Base(18)
{
}
};
or write its own conversion operator in Derived.
I have a base class with a member variable (preferably private) and I need to enforce derived classes to initialize it with a value based on their implementation; much like a pure virtual function.
To clarify, I want to declare a member in Base, have derived classes initialize it, and if they don't they get a compiler error. In the following code, I declared default constructor of Base to be protected. Then declared default constructor of Derived to be private.
class Base {
private:
int _size;
protected:
Base(){}
/* pure virtual methods */
public:
Base(int size) : _size(size){} // must enforce derived to call this.
virtual ~Base(){}
/* more pure virtual methods */
};
class Derived : public Base {
private:
Derived() {}
public:
Derived(int size) : Base(size) {
//Base::Base(size);
}
};
int main()
{
Derived* d1 = new Derived(); // throws an error as needed:
// "Cannot access private member declared in class 'Derived'"
Derived* d2 = new Derived; // throws an error as needed:
// "Cannot access private member declared in class 'Derived'"
Derived* d3 = new Derived(5); // works as needed
return 0;
}
The problem with the above code is that if another definition of Derived doesn't hide the default constructor. I'm still stuck with an uninitialized Base::_size.
I don't know if there is another way to go about this other than inheritance, because I still need derived classes to implement their own behavior for several methods declared in Base.
Any pointers are appreciated.
After the confusion about calling a base class ctor and default ctors, maybe the solution is just to not have a default ctor in Base?
class Base {
private:
int _size;
public:
// no default ctor
Base(int size) : _size(size) {} // must enforce derived to call this.
virtual ~Base(){}
/* more pure virtual methods */
};
class Derived : public Base {
public:
// no default ctor
Derived(int size) : Base(size){
}
// examplary default ctor:
//Derived() : Base(42) {}
};
int main()
{
Derived d1; // error: no default ctor
Derived* d2 = new Derived; // same, but why use the free store?
Derived d3(5); // works as needed
Derived* d4 = new Derived(5); // same, but why use the free store?
return 0;
}
To be explicit about not having a default ctor, one could use
class Base {
/* ... */
Base() = delete;
/* ... */
};
Use a constructor
class Base1 {
protected:
Base1(int forward) {
thingYouWantToHide = forward;
}
private:
int thingYouWantToHide;
};
class Derived1: public Base1 {
public:
Derived1(): Base1(5) {}
};
class Base2 {
private:
int value;
protected:
Base2() {
value = calledToGet();
}
virtual int calledToGet() = 0;
virtual ~Base2() {} //shut compiler warnings up
};
class Derived2: public Base2 {
virtual int calledToGet() {
return 5;
}
};
int main(int,char**) {
Derived1 a;
Derived2 b;
return 0;
}
You may think Derived2 will work, but remember Derived2 is not constructed until Base2 is, so that virtual is an undefined reference when Base2 is being constructed.
You should use the first case, type-traits if it is a constant (static const) or fundamental to the type.
I am getting the following error in C++:
error C2614: 'ChildClass' : illegal member initialization: 'var1' is
not a base or member
Class Base
{
protected:
int var1;
public:
Base()
{
var1=0;
}
}
class Child : public Base
{
int chld;
public:
Child() : var1(0)
{
chld=1;
}
}
I feel what I have done is as per OO protocol.
Here var1 is a data member of Base class with protected as the access specifier. So It can be inherited and it would become private in child.
Don't understand why am I getting the error?
It doesn't work for the exact reason the error message provides you: you can only use initializer lists with direct members or base classes.
In your case, you don't even need to initialize var1, since Base::Base() will be called by Child's constructor, which will set var1 to 0.
If you want a different value, you'll have to overload Base constructor and call it explicitly:
class Base
{
protected:
int var1;
public:
Base() : var1(0)
{
}
Base(int x) : var1(x)
{
}
};
class Child:public Base
{
int chld;
public:
Child(): Base(42) , chld(1)
{
}
};
You can't initialize a member of a base class, only of the current class. Use a parameterized constructor in the base class.
Class Base
{
protected:
int var1;
Base( int var ) : var1(var)
{}
public:
Base()
{
var1=0;
}
};
class Child:public Base
{
int chld;
public:
Child():Base(0)
{
chld=1;
}
};