I am using Visual Studio 2019 (v16.10.3) with /std:c++latest and this compiles:
class Base
{
public:
Base(int x) {}
};
class Derived : public Base
{
// no constructors declared in Derived
};
int main() {
Derived d(5);
}
For the previous versions of the standard I have to declare inherited constructors with the using directive:
class Derived : public Base
{
using Base::Base;
};
Is this something new that was put in C++20 or is it some Microsoft specific thing?
Is this something new that was put in C++20 or is it some Microsoft specific thing?
Not with relation to inherited constructors. What changed is that aggregate initialization may use parenthesis under certain conditions. Derived is considered an aggregate on account of having no private parts, so we initialize its bases and members directly.
It even works when we add a public member:
class Base
{
public:
Base(int ) {}
};
struct Derived : public Base
{
// no constructors declared in Derived
int y;
};
int main() {
Derived d(5, 4);
}
Live
This is the result of C++17 allowing classes with base classes to still be considered aggregates combined with C++20 rules allowing aggregate initialization to work with () constructor syntax if the values in the parentheses would work (and wouldn't call a constructor). So while it looks like a constructor call, it's actually aggregate initialization.
Derived doesn't have a constructor matching Base's constructor; you're merely initializing the aggregate Derived by providing a valid initializer for its subobject Base.
Related
In the following code you can see that I'm inheriting the base class ctors into the derived class under the "private" access specifier. My initial thought would be that these would adapt to the access specifiers that I have given (here "private") and hence not be available to use, but I seem to be mistaken. What are the rules for inheriting base class constructors and operators regarding access specifiers in the derived class?
Demo
#include <cstdio>
class base
{
public:
base(int i) {
printf("Public base class ctor called!");
}
private:
base(bool b) {
printf("Private base class ctor called!");
}
};
class derived final : public base
{
private:
using base::base;
};
int main()
{
derived d(2);
// issues "is private within this context"
// derived e(true);
}
Outputs:
Public base class ctor called!
(expected derived ctor to be "private" in this context)
From The C++ 17 Standard (10.3.3 The using declaration)
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.
So as in your example the first constructor (with the parameter of the type int) is accessible in the base class then the corresponding inherited constructor in the derived class is also accessible.
On the other hand, the second constructor (with the parameter of the type bool) is private. So for the second object definition of the derived class
derived e(true);
the compiler will issue an error.
In fact inherited constructors also inherit access controls.
For the following code:
struct Base
{
protected:
Base(){}
Base(int) {}
};
struct Derive : public Base
{
public:
using Base::Base;
};
int main()
{
Derive d1;
Derive d2(3);
}
Seems d1 can be constructed correctly, but d2 cannot be constructed.
SO my question is: Why using Base::Base can only change the default constructor to public and keep the constructor with a int parameter as protected?
Thanks a lot!
If you want to send parameters to base class constructor, you need to send the parameters through derived class constructor only.
using in your code is no use like below. Below function works fine.
#include <iostream>
using namespace std;
struct Base
{
protected:
Base() {}
Base(int) {}
};
struct Derive : public Base
{
public:
//using Base::Base;
};
int main()
{
Derive d1;
//Derive d2(3);
return 0;
}
Note that I didn't use 'using'. But as I am not passing any parameters to 'd1' object, through derived class default constructor, base class constructor (constructor with no arguments) will be called. But if you want to send parameters to base class, you need to send it through derived class only like below.
#include <iostream>
using namespace std;
struct Base
{
protected:
Base() {}
Base(int y) {};
};
struct Derive : public Base
{
public:
Derive()
{
};
Derive(int x) : Base(x) {
}
};
int main()
{
Derive d1;
Derive d2(3);
return 0;
}
The using-declaration makes the Base constructors visible for overload resolution but with the same accessibility that the constructor has in the base class.
If the using-declaration refers to a constructor of a direct base of the class being defined (e.g. using Base::Base;), all constructors of that base (ignoring member access) are made visible to overload resolution when initializing the derived class.
If overload resolution selects an inherited constructor, it is accessible if it would be accessible when used to construct an object of the corresponding base class: the accessibility of the using-declaration that introduced it is ignored.
Base::Base(int) is still protected in Derive, not public.
It's not clear to me why Derive::Derive() works, however. Apparently the using-declaration doesn't count as a user-declared constructor, meaning that it still has the implicitly-declared default constructor which is public. Therefore, it is preferred in overload resolution to the base class constructor introduced by your using declaration:
As with using-declarations for any other non-static member functions, if an inherited constructor matches the signature of one of the constructors of Derived, it is hidden from lookup by the version found in Derived.
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.
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 :)]
Consider:
class A
{
protected:
A(int) {}
void f(int) {}
public:
A() {}
};
class B : public A
{
public:
using A::A;
using A::f;
};
int main()
{
B().f(1); // ok
B(1); // error: 'A::A(int)' is protected within this context
}
Why can't an inherited protected constructor be made public, while an inherited protected member function can?
Unlike other members, the accessibility of the using-declaration that introduced the inherited constructor is ignored.
[namespace.udecl]/19,
(emphasis mine)
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.
Actually, the inherited constructor can be made public, but not just the way you wrote it. You can define your B class as follows:
class B : public A {
public:
B() {}
B(int x) : A(x) {} // instead of using A::A(int)
using A::f;
};
(see it on GodBolt)
Perhaps the standard committee thought that saying using A::A would be a bit ambiguous, since a constructor of the base class is not exactly the same thing as a constructor of the subclass.
It's easier to explain it in code:
class A {
protected:
A(int i) {}
void foo() {}
};
class B : public A {
public:
B() : A(0) {}
using A::A;
using A::foo;
};
int main()
{
B b1;
// [protected] A::foo => [public] B::foo
b1.foo(); // Ok
// [protected] A::A(int) => [protected] B::B(int)
B b2(0); // cannot access protected member
}
I tried the code in VS2015. I could change the access levels of member functions with using declarations, while I couldn't do the same on constructors. That's weird to me. Does anyone have an idea why they design it works like this?
Constructors are not generated in this case, instead they are inherited (actually from a base class).
According with the documentation, when inheriting a constructor:
It has the same access as the corresponding base constructor
Where access indicates access specifiers.
On the other side, for member methods:
Using-declaration introduces a member of a base class into the derived class definition, such as to expose a protected member of base as public member of derived.
In this case, access can be explicitly changed.
That's why you can change the access levels of member functions with using declarations, while you cannot do the same on constructors.