Today I've learned a new shocking reality, all popular compilers (those I could put my hands on, on godbolt.org) are good with this code (it compiles) and I can't explain why:
class A
{
protected:
A()
{ }
};
class B : private A
{
using A::A;
};
int main()
{
auto b = B{};
return 0;
}
My reasoning: it should fail at auto b = B{};
, since using
declaration is in private scope, therefore that's where the constructors, implicitly provided by the compiler, as the result of that using
, should go.
Be it any other member: function or variable, it's access modifier would be determined, based on where is the using
declaration is placed (public
/protected
/private
section).
But, now, this doesn't compile:
class A
{
protected:
A(int)
{ }
};
class B : private A
{
using A::A;
};
int main()
{
auto b = B{1};
return 0;
}
And that's predictable and intuitive:
<source>:15:14: error: calling a protected constructor of class 'A'
auto b = B{1};
^
<source>:4:5: note: declared protected here
A(int)
But, unfortunately, it doesn't compile (as IS intuitive I believe) for other reasons, because this doesn't as well:
class A
{
protected:
A(int)
{ }
};
class B : public A
{
public:
using A::A;
};
int main()
{
auto b = B{1};
return 0;
}
It seems that using
declaration is either badly worded or badly understood. Unfortunately, many compilers (some of them, fortunately, not longer, in a HEAD) also struggle with friend
permissions:
class C;
class A
{
friend class C;
protected:
A(int)
{ }
};
class B : public A
{
public:
using A::A;
};
class C
{
public:
B make_b()
{
return B{1};
}
};
int main()
{
auto b = C{}.make_b();
return 0;
}
Can some language lawyer analyse this and shed some light? Am I wrong with my assumptions and this is how it should be?
If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted
There are no user-declared constructors for class B
. The constructor that B
inherits from A
is not a constructor for B
, it is a constructor for A
. Inherited constructors are considered when looking up constructors for the derived class, but they are still not constructors for the derived class.
The standard never explicitly says that inheriting constructors does or does not create similar constructors for the derived class. The standard does say that the constructors for the base class are made available for lookup and overload resolution as if they were constructors for the derived class. This IMO means that they are not considered constructors for the derived class, though it would be better if the standard explicitly said that. At any rate, the compilers seem to interpret it this way.
Edit This is a change from C++14, where inherited constructors were injected ino the derived class. Even in C+14, these constructors were implicitly declared and not user-declared.
Thus B
has a public implicitly declared as defaulted default constructor, regardless of whatever it inherits from A
.
Base-class constructors considered because of a using-declarator are accessible if they would be accessible when used to construct an object of the base class; the accessibility of the using-declaration is ignored.
Thus A::A(int)
is not accessible when constructing B
, even though the using
declaration that imports it is accessible.