This question already has an answer here:
C++11 inheriting constructors and access modifiers
(1 answer)
Closed 3 years ago.
class A {
protected:
A(int) {}
};
struct B : public A {
public:
using A::A;
};
void print(B b) {}
int main(int argc, char** argv) {
print(1);
return 0;
}
This code does not compile... Even with 'using A::A' in struct B public section, B still does not have a public constructor accepting an int (but it has a protected one).
It seems that :
I can inherit public constructor with 'using'
I can re-declare as public in derived class any method defined in base class (private or protected or else)
BUT I cannot do the same with a constructor : no way to alter its visibility with 'using'
Why ?
This is intentional. Note that A::A inherits every constructor, not just the one you are expecting. Applying the using's access modifier to every inherited constructor would likely be too far reaching. Instead, it simply makes them available for overload resolution.
From cppreference :
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.
If you want to call A::A(int) when constructing a B, you can implement B::B(int) yourself such that it calls it.
If the current access specifier of the using declaration changed the access specifier of the "inherited" constructors, then there would be no way for separate "inherited" constructors to have different access specifier.
If the base class does have multiple constructors with differing access, then it would be typically desirable for the accessibility to remain in the derived class. The rules of using make this possible.
Related
The following code doesn't compile:
class C
{
private:
int m_x;
protected:
C(int t_x) : m_x(t_x) { }
};
class D : public C
{
public:
using C::C;
};
int main(int argc, char **argv)
{
D o(0);
}
The compiler's objection is that the constructor for C is declared protected, meaning that I can't access it from main. In other words, it seems like the using declaration drags the original visibility of the identifier with it, despite the fact that it lives in the public block.
Two questions:
Why does this happen? (Both in terms of the rules for how this works, and the rationale for making those rules).
Is there any way I can get around this without explicitly writing a constructor for D?
This is a subtle one. In C++, employing the using keyword on a base class constructor is called inheriting constructors and works differently than what a using keyword typically does. Specifically, note that
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.
(Emphasis mine. Source)
In other words, the fact that you've included the using declaration in a public section doesn't actually make those constructors public.
I believe that, in this case, you may have to define your own constructors to match the base type constructors.
statement using C::C; is not about increasing visibility, but, about since c++11
feature called inheriting constructors, making constructor of base class C invoking when derived D is constructed. You cant change access modifier of constructor that way. But, you can change access modifier, of any function by explicitly redeclaring it in a derived class with the different access modifier.
I have a class that looks something like this:
class A
{
public:
void foo(int arg) { foo(arg, false); }
private:
void foo(int arg, bool flag) {}
};
It is built this way because I want foo's flag argument to only be false when called from outside A. I want to inherit it privately, but allow calling foo:
class B : private A
{
public:
using A::foo;
};
However, this fails because the using declaraion attempts to bring all the overloads of foo into scope, including the private one, which the the compiler rightly rejects.
This isn't hard to fix, I can either:
Change the accessibility of A::foo(int, bool) to protected or public
Inherit A publicly; only public overloads of foo will be inherited
Change the name of A::foo(int, bool) so that the using declaration does not attempt to bring it into scope
This is a small, private project, and besides, that overload is only called inside A. So fixing the problem is a non-issue here. (I'm just going to rename the private overload.)
But it doesn't feel like it should be necessary. Why does the using declaration attempt to bring the non-accessible methods into scope? Is this particular case just not covered by the standard? Is there a way to fix this besides the methods I listed?
You can also redefine the overload you want and have it forward its argument to the function in A:
class B : private A
{
public:
void foo(int arg) { A::foo(arg); }
};
The using declaration is just too blunt a tool in this case. It brings function names into the derived class scope. And when the name refers to something private, it chokes. It can't distinguish overloads. The standard requires the names introduced by a using declaration to be accessible:
[namespace.udecl]/17
In a using-declarator that does not name a constructor, all members of
the set of introduced declarations shall be accessible. In a
using-declarator that names a constructor, no access check is
performed. In particular, if a derived class uses a using-declarator
to access a member of a base class, the member name shall be
accessible. If the name is that of an overloaded member function, then
all functions named shall be accessible. The base class members
mentioned by a using-declarator shall be visible in the scope of at
least one of the direct base classes of the class where the
using-declarator is specified.
The forwarding function can also be templated. So one won't need to redefine each function they want to expose individually.
class B : private A
{
public:
template<typename ...Args>
void foo(Args ...args) { A::foo(args...); }
};
It's "catch-all" like the using declaration, except the access specifier is checked only on template instantiation, i.e. when the function is called. So the template will be ill-formed based on its scope and whether or not the member in A is accessible there.
I found the following extract from Scott Meyer's Effective C++ which is related to your predicament (with emphasis added):
Item 33: Avoid hiding inherited names.
...
This means that if you inherit from a base class with overloaded functions
and you want to redefine or override only some of them, you need
to include a using declaration for each name you’d otherwise be hiding.
If you don’t, some of the names you’d like to inherit will be hidden.
...
It’s conceivable that you sometimes won’t want to inherit all the functions
from your base classes. Under public inheritance, this should
never be the case, because, again, it violates public inheritance’s is-a
relationship between base and derived classes. (That’s why the using
declarations above are in the public part of the derived class: names
that are public in a base class should also be public in a publicly
derived class.)
Under private inheritance, however, it can
make sense. For example, suppose Derived privately inherits from
Base, and the only version of the function that Derived wants to inherit is the
one taking no parameters. A using declaration won’t do the trick here,
because a using declaration makes all inherited functions with a given
name visible in the derived class.
No, this is a case for a different technique, namely, a simple forwarding function:
class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
... // as before
};
class Derived: private Base {
public:
virtual void mf1() // forwarding function; implicitly
{
Base::mf1(); } // inline
};
}
The using declaration for the base constructor is private, but the class can still be constructed. Why?
Accessibility works differently for the operator[]'s using declaration which must be public.
#include <vector>
template<typename T>
class Vec : std::vector<T>
{
private:
using std::vector<T>::vector; // Works, even if private. Why?
public:
using std::vector<T>::operator[]; // must be public
};
int main(){
Vec<int> vec = {2, 2};
auto test = vec[1];
}
What if I wanted the constructor to be private? Could it be done with a using declaration?
Using-declarations for base class constructors keep the same accessibility as the base class, regardless of the accessibility of the base class. From [namespace.udecl]:
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
emphasis added
In plain English, from cppreference:
It has the same access as the corresponding base constructor.
If you want the "inherited" constructors to be private, you have to manually specify the constructors. You cannot do this with a using-declaration.
using reference states that an inherited constructor
has the same access as the corresponding base constructor.
It further gives some hint on the rationale behind this:
It is constexpr if the user-defined constructor would have satisfied constexpr constructor requirements. It is deleted if the corresponding base constructor is deleted or if a defaulted default constructor would be deleted
Apparently, you cannot explicitly constexpr or delete an inherited constructor, so those characteristics are simply inherited. Same goes for access levels.
I'm about to create an exception class hierarchy which conceptually looks somewhat like this:
#include <iostream>
#include <stdexcept>
class ExceptionBase : public std::runtime_error {
public:
ExceptionBase( const char * msg ) : std::runtime_error(msg) {}
};
class OperationFailure : virtual public ExceptionBase {
public:
using ExceptionBase::ExceptionBase;
};
class FileDoesNotExistError : virtual public ExceptionBase {
public:
using ExceptionBase::ExceptionBase;
};
class OperationFailedBecauseFileDoesNotExistError
: public OperationFailure, FileDoesNotExistError {
public:
using ExceptionBase::ExceptionBase; // does not compile
};
int main() {
OperationFailedBecauseFileDoesNotExistError e("Hello world!\n");
std::cout << e.what();
}
All constructors should look the same as the constructor of the ExceptionBase class. The derived exceptions only differ concerning their type, there's no added functionality otherwise. The last exception type mentioned in the above code should also have these constructors. Is this possible using the inheriting constructors feature of the C++11 standard? If that is not possible: what are alternatives?
(By the way: In the above code the classes OperationFailure and FileDoesNotExistError did not compile with gcc 4.8, but with clang 3.4. Apparently, gcc rejects inheriting constructors for virtual bases. It would be interesting to know who's right here. Both compilers rejected the class OperationFailedBecauseFileDoesNotExistError, because the inheriting constructor does not inherit from a direct base.)
When the using-declaration is used to inherit constructors, it requires a direct base class [namespace.udecl]/3
If such a using-declaration names a constructor, the nested-name-specifier shall name a direct base class of the class being defined; otherwise it introduces the set of declarations found by member name lookup.
I.e. in your case, the using-declaration in OperationFailedBecauseFileDoesNotExistError doesn't inherit, but re-declares (as an alias), or unhides, the name of the ctor of ExceptionBase.
You'll have to write a non-inheriting ctor for OperationFailedBecauseFileDoesNotExistError.
By the way, this is fine for non-virtual base classes: The using-declaration for inheriting ctors is rewritten as:
//using ExceptionBase::ExceptionBase;
OperationFailure(char const * msg)
: ExceptionBase( static_cast<const char*&&>(msg) )
{}
As you may only initialize a direct base class (or virtual base class) in the mem-initializer-list, it makes sense for non-virtual base classes to restrict the using-declaration to inherit ctors only from direct base classes.
The authors of the inheriting ctors proposal have been aware that this breaks support for virtual base class ctors, see N2540:
Typically, inheriting constructor definitions for classes with virtual bases will be ill-formed, unless the virtual base supports default initialization, or the virtual base is a direct base, and named as the base forwarded-to. Likewise, all data members and other direct bases must support default initialization, or any attempt to use a inheriting constructor will be ill-formed. Note: ill-formed when used, not declared.
Inhering constructors is like introducing wrapper functions for all the constructors you have specified. In your case, you must call the specific constructors of both OperationFailure and FileDoesNotExistError but the introduced wrappers will only call either of them.
I just checked the latest C++11 draft (section 12.9) but it doesn't really explicitly cover your case.
consider the following code:
class A
{
friend class B;
friend class C;
};
class B: virtual private A
{
};
class C: private B
{
};
int main()
{
C x; //OK default constructor generated by compiler
C y = x; //compiler error: copy-constructor unavailable in C
y = x; //compiler error: assignment operator unavailable in C
}
The MSVC9.0 (the C++ compiler of Visual Studio 2008) does generate the default constructor but is unable to generate copy and assignment operators for C although C is a friend of A. Is this the expected behavior or is this a Microsoft bug? I think the latter is the case, and if I am right, can anyone point to an article/forum/... where this issue is discussed or where microsoft has reacted to this bug. Thank you in advance.
P.S. Incidentally, if BOTH private inheritances are changed to protected, everything works
P.P.S. I need a proof, that the above code is legal OR illegal. It was indeed intended that a class with a virtual private base could not be derived from, as I understand. But they seem to have missed the friend part. So... here it goes, my first bounty :)
The way I interpret the Standard, the sample code is well-formed. (And yes, the friend declarations make a big difference from the thing #Steve Townsend quoted.)
11.2p1: If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class.
11.2p4: A member m is accessible when named in class N if
m as a member of N is public, or
m as a member of N is private, and the reference occurs in a member or friend of class N, or
m as a member of N is protected, and the reference occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected, or
there exists a base class B of N that is accessible at the point of reference, and m is accessible when named in class B.
11.4p1: A friend of a class is a function or class that is not a member of the class but is permitted to use the private and protected member names from the class.
There are no statements in Clause 11 (Member access control) which imply that a friend of a class ever has fewer access permissions than the class which befriended it. Note that "accessible" is only defined in the context of a specific class. Although we sometimes talk about a member or base class being "accessible" or "inaccessible" in general, it would be more accurate to talk about whether it is "accessible in all contexts" or "accessible in all classes" (as is the case when only public is used).
Now for the parts which describe checks on access control in automatically defined methods.
12.1p7: An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object of its class type (1.8). The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with an empty mem-initializer-list (12.6.2) and an empty function body. If that user-written default constructor would be ill-formed, the program is ill-formed.
12.6.2p6: All sub-objects representing virtual base classes are initialized by the constructor of the most derived class (1.8). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed.
12.4p5: An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type (3.7). A program is ill-formed if the class for which a destructor is implicitly defined has:
a non-static data member of class type (or array thereof) with an inaccessible destructor, or
a base class with an inaccessible destructor.
12.8p7: An implicitly-declared copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type. [Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2).] A program is ill-formed if the class for which a copy constructor is implicitly defined has:
a nonstatic data member of class type (or array thereof) with an inaccessible or ambiguous copy constructor, or
a base class with an inaccessible or ambiguous copy constructor.
12.8p12: A program is ill-formed if the class for which a copy assignment operator is implicitly defined has:
a nonstatic data member of const type, or
a nonstatic data member of reference type, or
a nonstatic data member of class type (or array thereof) with an inaccessible copy assignment operator, or
a base class with an inaccessible copy assignment operator.
All these requirements mentioning "inaccessible" or "accessible" must be interpreted in the context of some class, and the only class that makes sense is the one for which a member function is implicitly defined.
In the original example, class A implicitly has public default constructor, destructor, copy constructor, and copy assignment operator. By 11.2p4, since class C is a friend of class A, all those members are accessible when named in class C. Therefore, access checks on those members of class A do not cause the implicit definitions of class C's default constructor, destructor, copy constructor, or copy assignment operator to be ill-formed.
Your code compiles fine with Comeau Online, and also with MinGW g++ 4.4.1.
I'm mentioning that just an "authority argument".
From a standards POV access is orthogonal to virtual inheritance. The only problem with virtual inheritance is that it's the most derived class that initializes the virtually-derived-from class further up the inheritance chain (or "as if"). In your case the most derived class has the required access to do that, and so the code should compile.
MSVC also has some other problems with virtual inheritance.
So, yes,
the code is valid, and
it's an MSVC compiler bug.
FYI, that bug is still present in MSVC 10.0. I found a bug report about a similar bug, confirmed by Microsoft. However, with just some cursory googling I couldn't find this particular bug.
Classes with virtual private base should not be derived from, per this at the C++ SWG. The compiler is doing the right thing (up to a point). The issue is not with the visibility of A from C, it's that C should not be allowed to be instantiated at all, implying that the bug is in the first (default) construction rather than the other lines.
Can a class with a private virtual base class be derived from?
Section: 11.2 [class.access.base]
Status: NAD Submitter: Jason
Merrill Date: unknown
class Foo { public: Foo() {} ~Foo() {} };
class A : virtual private Foo { public: A() {} ~A() {} };
class Bar : public A { public: Bar() {} ~Bar() {} };
~Bar() calls
~Foo(), which is ill-formed due to
access violation, right? (Bar's
constructor has the same problem since
it needs to call Foo's constructor.)
There seems to be some disagreement
among compilers. Sun, IBM and g++
reject the testcase, EDG and HP accept
it. Perhaps this case should be
clarified by a note in the draft. In
short, it looks like a class with a
virtual private base can't be derived
from.
Rationale: This is what was intended.
btw the Visual C++ v10 compiler behaviour is the same as noted in the question. Removing virtual from the inheritance of A in B fixes the problem.