Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing protected constructor in parent class

As the title says, I'm wondering if there's a way to expose a protected constructor in a derived class (that is, to change access from protected to public). Consider the following (contrived) example:

struct A {
    int a;
    int b;
protected:
    A(int, int) {};
    void do_something();
};

struct B : A {
    B() : A(0, 0) {};
    using A::A;
    using A::do_something;
};

int main() {
    B b(0, 0); // error: 'A::A(int, int)' is protected
    //B b{}; // no errors
    b.do_something();
}

So one can change the access of protected member functions, but not of constructors? If so, what is the rationale for the restriction?


Workaround: Variadic template with argument forwarding can serve as a workaround that would perform identically.


Speculation for the rationale: by default the constructor of base classes are not "inherited" in the regular sense; the constructor of derived class must construct the base class instance(s) first. When using is used upon regular member functions, it introduces the functions into the current declarative region thus changing access; on constructors, using only brings base constructor to the "normal inheritance" level (identical to other member functions without using). Because only in some cases is constructor inheritance (reuse of base constructors) useful, the standard committee people designated using X::X syntax for constructor inheritance rather than the stronger namespace teleporting.

like image 314
YiFei Avatar asked Jun 12 '20 19:06

YiFei


1 Answers

What you are trying to do is to call the constructor B, with two parameters, but it doesn't take any.

The error you get, is about the constructor of B, being protected, but if you look carefully, that is not the constructor you declared:

error: 'B::B(int, int)' is protected

Why is that? Because the compiler is trying to resolve your call to something valid, and it finds the base class constructor to match the signature. But it is still protected so it fails.

What you need to do, to "relaunch" the protected constructor to an inherited class is to provide a matching constructor:

struct B : A {
    B() : A(0, 0) {};
    B(int a, int b) : A(a, b) {};
    using A::A;
    using A::do_something;
};

int main() {
    B b;
    B b2(1, 2);
    //b.do_something();
}

http://cpp.sh/9tvik

The reason why, you cannot change the accessor level of a constructor the same way you change it for a method, is most probably to be searched into the non-virtual nature of the constructors. [citation needed :)]

like image 67
crsn Avatar answered Oct 06 '22 18:10

crsn