I need a workaround or a nice solution for initializing a bunch of constant class variables in base and subclass
The problem is simple, I got a baseclass with two constructor and the same two constructor in the subclass
class BASE {
int a;
int func(float x) { // just a dummy to show that this function
return (a = (int) x) + 2; // modifies a
}
public:
const int b;
BASE(const int b) : b(b) {} // doesn't use a
BASE(const float x) : a(0), b(func(x)) {}
};
struct SUB: public BASE {
const int c;
const int d;
SUB(const int b) : BASE(b), c(b), d(c + 3) {}
SUB(const float x) : BASE(x), c(b), d(c + 3) {}
};
The subclass needs to call the constructor from BASE to initialize the class variables from BASE after that the sub class initialize the remaining variables
So far so good but the problem is that both constructor from SUB do the exactly same except calling a different constructor from BASE
I want something like that
SUB() : c(b), d(c + 3) {} // BASE() needs to be defined
SUB(const int b) : BASE(b), SUB() {}
SUB(const float x) : BASE(x), SUB() {}
but that doesn't work because "a call to a delegating constructor shall be the only member-initializer" ...
Moving everything outside the initializer list doesn't work because these are const class variables
You can create a "forwarding constructor" for your derived class:
struct SUB: public BASE {
const int c;
const int d;
template <class... T>
SUB(T&&... a) : BASE(std::forward<T>(a)...), c(b), d(c + 3) {}
};
This will accept arbitrary arguments and forward them on to BASE. Of course, it will only compile when called with arguments which are valid for BASE, but that holds for every case.
If you want/need to be super-correct, you can condition the constructor using SFINAE on somethin like std::is_constructible<BASE, T&&...>, but I wouldn't bother.
Unfortunately that's the rules of the language, if you have a delegating constructor it must be the only part of the initializer list.
You need to work around that limitation, for example by duplicating the default constructor initializer list:
SUB(const int b) : BASE(b), c(b), d(c + 3) {}
Related
Why can't I do this?
class A
{
public:
int a, b;
};
class B : public A
{
B() : A(), a(0), b(0)
{
}
};
You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:
class A
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};
class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};
Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).
Try:
class A
{
int a, b;
protected: // or public:
A(int a, int b): a(a), b(b) {}
};
class B : public A
{
B() : A(0, 0) {}
};
Somehow, no one listed the simplest way:
class A
{
public:
int a, b;
};
class B : public A
{
B()
{
a = 0;
b = 0;
}
};
You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.
# include<stdio.h>
# include<iostream>
# include<conio.h>
using namespace std;
class Base{
public:
Base(int i, float f, double d): i(i), f(f), d(d)
{
}
virtual void Show()=0;
protected:
int i;
float f;
double d;
};
class Derived: public Base{
public:
Derived(int i, float f, double d): Base( i, f, d)
{
}
void Show()
{
cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
}
};
int main(){
Base * b = new Derived(10, 1.2, 3.89);
b->Show();
return 0;
}
It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.
Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.
How can you get this done? Like this:
class A
{
public:
A(int a, int b) : a_(a), b_(b) {};
int a_, b_;
};
class B : public A
{
public:
B() : A(0,0)
{
}
};
While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation
Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members
Why can't I do this?
class A
{
public:
int a, b;
};
class B : public A
{
B() : A(), a(0), b(0)
{
}
};
You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:
class A
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};
class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};
Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).
Try:
class A
{
int a, b;
protected: // or public:
A(int a, int b): a(a), b(b) {}
};
class B : public A
{
B() : A(0, 0) {}
};
Somehow, no one listed the simplest way:
class A
{
public:
int a, b;
};
class B : public A
{
B()
{
a = 0;
b = 0;
}
};
You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.
# include<stdio.h>
# include<iostream>
# include<conio.h>
using namespace std;
class Base{
public:
Base(int i, float f, double d): i(i), f(f), d(d)
{
}
virtual void Show()=0;
protected:
int i;
float f;
double d;
};
class Derived: public Base{
public:
Derived(int i, float f, double d): Base( i, f, d)
{
}
void Show()
{
cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
}
};
int main(){
Base * b = new Derived(10, 1.2, 3.89);
b->Show();
return 0;
}
It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.
Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.
How can you get this done? Like this:
class A
{
public:
A(int a, int b) : a_(a), b_(b) {};
int a_, b_;
};
class B : public A
{
public:
B() : A(0,0)
{
}
};
While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation
Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members
Is there a way to inherit private constructors with the using statement?
I tried adding a friend statement, but it did not seem to work.
struct B;
struct A
{
private:
A(int x) {}
A(int y, double z) {}
friend struct B;
};
struct B : public A
{
public:
using A::A;
//B(int x) : A(x) {}
//B(int x, int y) : A(x, y) {}
};
void demo()
{
B b1(5); // does not compile - implicit constructor is not accessible
B b2(4, 9.0);
}
I wonder if there is any way to do it with using, since if I explicitly create the delegating constructors the friend statement works, so this seams inconsistent:
struct B;
struct A
{
private:
A(int x) {}
A(int y, double z) {}
friend struct B;
};
struct B : public A
{
public:
B(int x) : A(x) {} // OK
B(int x, int y) : A(x, y) {}
};
void demo()
{
B b1(5); // OK
B b2(4, 9.0);
}
Accessibility of inherited constructors is not affected by the accessibility of the using declaration that introduces them.
[namespace.udecl] (emphasis mine)
19 A synonym created by a using-declaration has the usual
accessibility for a member-declaration. A using-declarator that names
a constructor does not create a synonym; instead, the additional
constructors are accessible if they would be accessible when used to
construct an object of the corresponding base class, and the
accessibility of the using-declaration is ignored.
Since you can't access the constructor of A in main, it isn't accessible to construct a B in main either (you would be able to use it in order to construct B's and A's inside the scope of B though, on account of the friendship). You'll need to spell out some sort of of forwarding c'tor explicitly for outside-the-class access. Since you seem to be after a "catch-all", a fairly simple solution can be
template<typename... Args>
B(Args&&... args) : A(std::forward<Args>(args)...) {}
I wonder if there is any way to do it with using, since if I explicitly create the delegating constructors the friend statement works
No, when using the base class constructors, they will keep the original protected or private access, so you need to implement them in the derived class if you'd like to make them public. Making B a friend doesn't change that. It only allows B to access the private members in A - both functions and variables.
If you'd like derived classes to be able to use certain private member functions, you usually make those member functions protected instead.
struct A {
protected:
A(int x) {}
A(int y, double z) {}
};
struct B : public A {
public:
B(int x) : A(x) {}
B(int y, double z) : A(y, z) {}
};
With this, you don't need to know about the derived classes when designing A and you also keep your private member variables in A private.
Based on #StoryTeller - Unslander Monica's answer, which explains that the (inherited) constructors are accessible where the base class constructors would be, here is a very partial solution which will work in some cases:
struct A
{
private:
A(int x) {};
A(int y, double z) {};
friend struct B;
};
struct B : public A
{
public:
friend void demo();
using A::A;
};
void demo()
{
B b1(5);
B b2(4, 9.0);
}
int main() {
demo();
}
I have the following setup:
class MyClass {
public:
static MyClass Clone(const MyClass& other) {
return MyClass(other, 10);
}
static MyClass CreateNormal(int x, int y) {
return MyClass(int x, int y);
}
// There are a few more factory functions.
private:
// Constructor 1
MyClass(int x, int y) : b_(x, y) {}
// Constructor 2
MyClass(const MyClass& other, int c) : b_(other.b_, c) {}
// And a lot more constructors.
// NotMyClass has its copy constructor deleted.
NotMyClass b_;
}
int main() {
MyClass A(1,2);
MyClass B = MyClass::Clone(A); // ERROR
}
Is there a way to get around this without modifying NotMyClass?
The error happens because the copy constructor of MyClass is implicitly deleted. Is there a way I can use std::Move() to solve this?
I cannot remove the factory functions as the actual class has many constructors and the factory functions are needed for clarity and understanding.
Assuming the availability of the c++11 standard, you could extend the lifetime of the rvalue reference returned by the factory method instead of constructing a new object, like so:
MyClass A(1, 2);
MyClass&& B = MyClass::Clone(A);
Addendum:
As noted by patatahooligan in a comment below, there might be a copy constructor invocation in your version of MyClass::Clone itself. This can/needs to be rewritten to
MyClass MyClass::Clone(const MyClass& other) {
return {other, 10};
}
All of this will be completely unecessary once C++17 comes with mandatory copy-elision comes around, so you can look forward to that.
In C++03 it is still possible to achive the desired outcome albeit the solutions are longer and require more code. I offer two alternatives:
Instead of creating a set of static methods, I would advise for using tag-dispatch to signal different constructor meanings. This works by overloading the constructor with type arguments which have clear names but hold no data.
struct clone_tag {};
class MyClass {
public:
MyClass(int x, int y) : b_(x, y) {}
MyClass(const MyClass& self, clone_tag) : b_(self.b_, 10) {}
private:
NotMyClass b_;
};
int main() {
MyClass A(1, 2);
MyClass B(A, clone_tag());
}
Or this could even go as far as a genuine factory pattern, where the building the factory and final construction of the object is seperated. This clearly needs more time to set up but could improve code quality, depending on the actual number of different constructor paths.
class MyClass {
public:
struct Clone {
Clone(const MyClass& self) : self(self) {}
const MyClass& self;
};
struct Init {
Init(int x, int y) : x(x), y(y) {}
int x, y;
};
MyClass(Init i) : b_(i.x, i.y) {}
MyClass(Clone c) : b_(c.self.b_ , 10) {}
private:
MyClass(const MyClass& other, int c) : b_(other.b_, c) {}
NotMyClass b_;
};
int main() {
MyClass A = MyClass::Init(1, 2);
MyClass B = MyClass::Clone(A);
}
On second thought I realize that giving three different alternatives could cause more confusion than a short answer which is less precise. I therefore tried to list them in order of needed refactoring work although this is only guess work about your real code base.
I have a question on how to use initializer list for constructors of a derived class that are inheriting from constructors of a base class.
This is the code that works:
class base {
public:
base():x(0) {}
base(int a):x(a) {}
private:
int x;
};
class derived : public base {
public:
derived():base():y(0) { y=0; }
derived(int a, int b):base(a) { y=b; }
private:
int y;
};
However, I want to use the member initializer list to initialize the variables directly, and this leads to an error:
class base {
public:
base():x(0) {}
base(int a):x(a) {}
private:
int x;
};
class derived : public base {
public:
//derived():base():y(0) {} //wrong
//derived(int a, int b):base(a):y(b) {} //wrong
derived():base(), y(0) {} // corrected
derived(int a, int b): base(a), y(b) {} //corrected
private:
int y;
};
What's the right syntax for constructors that inherits from another constructor to use initializer list?
Thanks :)
As noted by Dieter, you can easily have many initializers in a constructor, they simply must be separated with comma (,) instead of column (:).
You class derived should then be :
class derived : public base {
public:
derived():base(),y(0) {}
derived(int a, int b):base(a),y(b) {}
private:
int y;
};
derivedClass::derivedClass(argumentsSetOne, argumentsSetTwo, .....) : baseClassOne(argumentsSetOne) , baseClassTwo(argumentsSetTwo) { }
order doesn't really matter...by that i mean, you can specify argumentsSetTwo before argumentsSetOne in the Derived Class's Constructor's arguments field. But again it should be same as specified in the prototype....