Typically the 'using' declaration is used to bring into scope some member functions of base classes that would otherwise be hidden. From that point of view it is only a mechanism for making accessible information more convenient to use.
However: the 'using' declaration can also be used to change access constraints (not only for functions but also for attributes). For example:
class C{
public:
int a;
void g(){ cout << "C:g()\n"; }
C() : a(0){}
};
class D : public C{
private:
using C::a;
using C::g;
public:
D() { a = 1; }
};
int main(void){
D d;
cout << d.a << endl; //error: a is inaccessible
C *cp = &d;
cout << cp->a << endl; //works
d.g(); //error: g is inaccessible
cp->g(); //works
return 0;
}
I think this limitation of access in the derived class is actually of no use, because you can always access g() and a from a pointer to the base class. So should't there be at least some kind of compiler warning? Or wouldn't it been even better to forbid such limitation of access by a derived class? The using declaration is not the only possibility to add constraints to access. It could also be done via overriding a base class' function an placing it in a section with more access constraints.
Are there some reasonable examples where it is indeed nessecary to limit access in such a way? If not I don't see why it should be allowed.
And another thing: at least with g++ the same code compiles well without the word 'using'. That means for the example above: it's possible to write C::a; and C::g; instead of using C::a; using C::g; Is the first only a shortcut for the latter or are there some subtle differences?
//EDIT:
so from the discussion and answers below my conclusion would be:
- it's allowed to limit access constraints in derived classes with public inheritance
- there are useful examples where it could be used
- it's use might cause problem in combination with templates (e.g. a derived class could not be a valid parameter for some template class/function any more although it's base is)
- a cleaner language design should not allow such use
- compiler could at least issue some kind of warning
With regard to your declaration without using: These are called "access declarations", and are deprecated. Here is the text from the Standard, from 11.3/1:
The access of a member of a base class can be changed in the derived class by mentioning its qualified-id in
the derived class declaration. Such mention is called an access declaration. The effect of an access declaration qualified-id; is defined to be equivalent to the declaration usingqualified-id; [Footnote: Access declarations are deprecated; member using-declarations (7.3.3) provide a better means of doing the same things. In earlier versions of the C++ language, access declarations were more limited; they were generalized and made equivalent to using-declarations - end footnote]
I would say that most often it's not good to change public members to private or protected members in the derived class, because this will violate the substitution principle: You know a base class has some functions, and if you cast to a derived class then you expect those functions to be callable too, because the derived class is-a base. And like you already mentioned, this invariant is already enforced anyway by the language allowing to convert (which working implicitly!) to a base class reference, or qualifying the function name, and then calling the (then public) function.
If you want to forbid someone calling a set of functions of the base, then i think this hints that containment (or in rare cases, private inheritance) is a better idea.
While the using declaration you showed does provide a mechanism to change access level (but only down), that is not the primary use of it in such a context. A using context there is primarily intended to allow access to functions that would otherwise be shadowed from the base class due to the language mechanics. E.g.
class A {
public:
void A();
void B();
};
class B {
public:
using A::B;
void B(int); //This would shadow A::B if not for a using declaration
};
The declaration
using C::a
brings "a" to the local naming scope so that you can later use "a" to refere to "C::a"; since that, "C::a" and "a" are interchangeable as long as you don't declare a local variable with name "a".
The declaration does not change access rights; you can access "a" in the subclass only because "a" is not private.
Related
I am a beginner in C++, I had a question regarding nested class in C++, how do you access the protected or private member of an enclosed class?
class A{
class B{
protected:
B(){};
};
B var; <=== error as the constructor B is protected
};
One solution is making constructor of B public, but this exposes it'a scope and other function can instantiate it, which I don't want. What's a way to do deal with this ?
You can use friend class:
class A
{
class B
{
friend class A; //<-- makes A a friend of B
protected:
B(){};
};
B var; //<-- OK
};
I believe the question that was asked does not get at the real issue. It is almost an XY problem, but fortunately it has sufficient hints for the real issue to be deduced. Still, in the interest of propriety, I will address the question that was asked before addressing what I believe is the real issue.
how do you access the protected or private member of an enclosed class?
The same way you grant such access when the classes are not nested: the class with the protected or private member declares the other class (or function) as a friend. Just be aware that this is a bit of a sledgehammer approach, so it is advisable to consider alternatives first.
One solution is making constructor of B public, but this exposes it's scope and other function can instantiate it, which I don't want.
No, it doesn't expose anything outside A. You have declared B as a private type within A, which means only A can reference B. With your current setup, there is no functional difference between making A a friend of B and making all members of B public. In either case, A has full access to B and nothing outside A knows that B exists.
While I suspect that B is supposed to be public, the OP has had days to correct this detail. In addition, the OP posted a new question after this possible oversight was pointed out, so I must conclude that the OP does not regard it as an oversight.
Now we get to what I believe is the real issue: how to construct a public nested class from the outer class without allowing public construction.
What's a way to do deal with this ?
If there is a possibility that B might become a public type at some point (a reasonable thing to guard against), you should consider alternatives to friendship. One alternative is to put the constructor of B under lock-and-key. This could be extended to other private members of B, but that is perhaps not necessary.
One advantage of this approach is that access is granted to only the constructor, not to all private members. This helps preserve encapsulation. Furthermore, this approach allows construction to be handed off to helper functions, such as those from the standard library. For example, an emplace_back method for a standard container could construct a B object as long as the method was given a key. This might be more convenient than relying on friendship, depending on how B objects are to be used.
The key
The "key" is a private class of A. It is likely going to be an empty class, as it does not need functionality. All the key needs to do is exist and be inaccessible outside A.
class A{
private:
class KeyForB {};
// Rest of the definition of A
};
The lock
The "lock" is a constructor that accepts the "key" as a parameter. This parameter will not be used by the constructor; all it does is signify that the caller is allowed to call the constructor.
class B{
public:
/* explicit */ B(KeyForB /* unnamed */) : B() {}
protected:
B() {}
};
There are a few details worth looking at. First, you might have noticed that B just used a private member of A! How? There is a special rule that says that nested classes are considered part of the outer class, and as such they can access the private (and protected) members of the outer class – no friend declaration necessary.
Second, the lock delegates to the protected constructor. This may seem odd, but it does serve a purpose. Not only does it keep the default constructor available to B, but also it allows the compiler to optimize away the lock and key. Consider what would happen if the constructor was so large that the compiler opted to not inline it. If a function is not inlined, then parameters cannot be optimized away. In this case, that would mean that a KeyForB would need to be constructed and placed on the call stack. In contrast, the lock is so simple that it should be inlined under any level of optimization. It gets inlined to a call to B() and the unused KeyForB is eliminated from the executable.
Third, there is a comment acknowledging that some coding guidelines recommend marking as explicit most constructors that take a single parameter. This is sound advice. However, in this case, the only reason to create a KeyForB object is to construct a B object. So in this case, it might be acceptable and convenient to leave this conversion implicit.
Putting it together
The only remaining piece is to write a constructor for A, as the compiler-provided default constructor is deleted because it would be ill-formed.
class A{
private:
class KeyForB {};
//public: // <-- possible future change
class B{
public:
B(KeyForB) : B() {}
protected:
B() {}
};
public:
// Default constructor
A() : var(KeyForB{}) {}
private:
B var;
};
It might be worth noting that if the type of var were to change from B to std::shared_ptr<B>, this approach would not run into "make_shared for friend class throwing error", as giving a key to make_shared would enable construction.
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.
Suppose we have two classes, one derived from other and it contains only one virtual non-pure method like this:
class Base
{
public:
virtual void method()
{
cout << "method() called from Base" << endl;
}
};
class Derived : public Base
{
public:
void method()
{
cout << "method() called from Derived" << endl;
}
};
int main()
{
Base *instance1 = new Derived();
instance1->method();
Derived derived;
derived.method();
}
Why, if we do not specify explicitly "public" access modifier in Derived class (so, compiler assumes that it's private) it wouldn't compile? Why instantiation way(via pointer and via regular variable) does affect to the visibility scope? Is there any rationale regarding this behavior?
Many thanks!
If you don't write public: then it's as if you've written private:. If you're asking why the language doesn't automatically switch to public: for Derived::method() (because it overrides Base::method() which is already public)… well, it could! This would be possible.
However, it would also be extremely confusing and misleading when reading the code. As a programmer I much prefer that my class definition isn't fundamentally altered behind my back like that.
The compiler throws out an error (as you've discovered) allowing me to make the change myself as I deem appropriate (whether that's making Derived::method() public, or Base::method() private!), which in my opinion is by far the best outcome.
If instead you're asking why a difference in visibility even matters at all, then that seems fairly obvious, does it not? If the user were calling a function through the Base class interface, in which method() is public, and this ended up automatically invoking a private function in a derived class, that violates the contract of the derived class. And because it's only the names that are protected by these visibility rules, the language adds an extra check for overridden methods to extend that protection as much as possible in the case of virtual function dispatch.
Something very similar is given as an example in the standard where the rules for these things are specified [class.access.virt]:
1 The access rules (Clause 11) for a virtual function are determined
by its declaration and are not affected by the rules for a function
that later overrides it.
[ Example:
class B {
public: virtual int f();
};
class D : public B
{
private: int f();
};
void f()
{
D d;
B* pb = &d;
D* pd = &d;
pb->f(); // OK: B::f() is public, // D::f() is invoked
pd->f(); // error: D::f() is private
}
— end example ]
2 Access is
checked at the call point using the type of the expression used to
denote the object for which the member function is called (B* in the
example above). The access of the member function in the class in
which it was defined (D in the example above) is in general not known.
The above answer both your questions:
Why does it not compile - as per rule 2 above access is checked using the type of the expression (i.e. the static not dynamic type)
What is the rationale - again as described above it is generally not known what the dynamic type is. To demonstrate, consider this: You can link new derived classes to an existing code defining a base class without recompiling this code: then clearly it would have no chance of determining the access controls of the derived (which didn't even exist when it was compiled).
I think i'm aware of accessibilty but I'm not sure if I understand visibility very clearly
For example:
class X
{
int x;
};
Here, 'x' is only visible in class and but accessible outside of class. If I'm correct, Can someone explain the text in that answer about how visibility is not controlled etc..?
(C++03/11.0) It should be noted that it is access to members and base classes that
is controlled, not their visibility. Names of members are still
visible, and implicit conversions to base classes are still
considered, when those members and base classes are inaccessible. The
interpretation of a given construct is established without regard to
access control. If the interpretation established makes use of
inaccessible member names or base classes, the construct is
ill-formed.
Perhaps this example helps:
class Bob
{
private:
int foo(int, int);
};
class David : Bob
{
void goo() {
int a = foo(1, 2); // #1
}
};
class Dani : Bob
{
void foo();
void goo() {
int a = foo(1, 2); // #2
}
};
On line #1, the name foo is visible, but the function which it names is not accessible (on account of being private to Bob). This is a compilation error, but the compiler knows that there is a potential function Bob::foo that would match, but isn't accessible.
On line #2, the name foo only refers to Dani::foo, while Bob::foo is not visible (because it is hidden), and so there is simply no matching function for the call foo(1, 2). This is also a compilation error, but this time the error is that there is no matching function at all for the call.
C++ has some esoteric feature concerning private class member names visibility and accessibility. By definition, a private class member name is only accessible by the class members and friends. However the rule of visibility can confuse many. They can be summarized as follows.
A private member's name is only accessible to other members and friends.
A private member is visible to all code that sees the class's definition. This means that its parameter types must be declared even if they can never be needed in this translation unit...
Overload resolution happens before accessibility checking.
In C++ today ("C++03" and earlier variants), the notions of accessibility and visibility are
independent. Members of classes and namespaces are visible whenever they are "in
scope" and there is no mechanism to reduce this visibility from the point of declaration.
Accessibility is only a parameter for class members and is orthogonal to the notion of
visibility. This latter observation is frequently surprising to novice C++ programmers. See this PDF.
Consider the following example.
#include < complex>
class Calc
{
public:
double Twice( double d );
private:
int Twice( int i );
std::complex Twice( std::complex c );
};
int main()
{
Calc c;
return c.Twice( 21 ); // error, Twice is inaccessible
}
When the compiler has to resolve the call to a function, it does three main things, in order:
Before doing anything else, the compiler searches for a scope that
has at least one entity named Twice and makes a list of candidates.
In this case, name lookup first looks in the scope of Calc to see if
there is at least one function named Twice; if there isn't, base
classes and enclosing namespaces will be considered in turn, one at a
time, until a scope having at least one candidate is found. In this
case, though, the very first scope the compiler looks in already has
an entity named Twice — in fact, it has three of them, and so that
trio becomes the set of candidates. (For more information about name
lookup in C++, with discussion about how it affects the way you
should package your classes and their interfaces
Next, the compiler performs overload resolution to pick the unique
best match out of the list of candidates. In this case, the argument
is 21, which is an int, and the available overloads take a double, an
int, and a complex. Clearly the int parameter is the best match for
the int argument (it's an exact match and no conversions are
required), and so Twice(int) is selected.
Finally, the compiler performs accessibility checking to determine
whether the selected function can be called.
Note that accessibility (defined by modifiers in C++) and visibility are independent. Visibility is based on the scoping rules of C++. A class member can be visible and inaccessible at the same time.
Static members as an example are visible globally through out the running of your application but accessible only with regard to the modifier applied to them.
As a note: when you declare a class, the scope is private by default (opposed to a struct where members are public by default.)
The variable member 'x' is only accessible by your class and its friends. No one else can ever access 'x' directly (it can indirectly if you have a function returning a reference to it, which is a really bad idea.)
The text you quoted talks about visibility to the compiler, so X::x exists, no matter what. It won't disappear just because it's private. The visibility is used to find the member you are referencing and the first that matches is returned. At that point the compiler checks the accessibility, if accessible, you're all good. If not it is ill-formed.
Note that I mentioned friends. That keyword makes all variable members accessible. When the compiler deals with a friends, it completely ignores all the protected and private keywords.
In most cases, that's a very easy process. It goes in order. Period.
Where it becomes more complicated is when you start using virtual functions: these can be made public, protected, and private and that can change depending on the class declaration... (A derives from B and makes a protected virtual function public; it's generally not a good idea, but C++ doesn't prevent you from doing so.) Of course this only applies to functions, not variable members, so that's a different subject.
That accessability and visibility are independet confuses especially in such situations:
class A
{
public:
void Foo(int i){
}
};
class B : public A
{
private:
void Foo(){
}
};
int main(){
B b{};
b.Foo(12);
}
Programmers from other languages would expect that A::Foo(int) would be callable because it is public. The point here is, that the private B::Foo hides the inherited proc.
This can be solved with a using declaration using A::Foo. But it becomes really hard in this sitation:
class A
{
public:
void Foo(int i){
}
};
class B : public A
{
public:
using A::Foo;
private:
void Foo(){
}
};
class C : public B
{
public:
using B::Foo;
private:
void Foo(char c){
}
}
int main(){
B b{};
b.Foo(12);
}
Using requires that there is NO private function. AFAIK the best way to solve it is to use some prefixes / suffixes for private or protected functions (like do_XXX() or do_XXX_internal).
In the STL private members are usually prefixed by a single underscore (these are reserved identifiers).
#include<iostream>
using namespace std;
class base
{
public:
virtual void add() {
cout << "hi";
}
};
class derived : public base
{
private:
void add() {
cout << "bye";
}
};
int main()
{
base *ptr;
ptr = new derived;
ptr->add();
return 0;
}
Output is bye
I dont have a problem with how this is implemented. I understand you use vtables and the vtable of derived contains the address of the new add() function. But add() is private shouldn't compiler generate an error when I try to access it outside the class? Somehow it doesn't seem right.
add() is only private in derived, but the static type you have is base* - thus the access restrictions of base apply.
In general you can't even know at compile time what the dynamic type of a pointer to base will be, it could e.g. change based on user input.
This is per C++03 §11.6:
The access rules (clause 11) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it.
[...] Access is checked at the call point using the type of the expression used to denote the object for which the member function is called [...]. The access of the member function in the class in which it was defined [...] is in general not known.
Access modifiers, such as public, private and protected are only enforced during compilation. When you call the function through a pointer to the base class, the compiler doesn't know that the pointer points to an instance of the derived class. According to the rules the compiler can infer from this expression, this call is valid.
It is usually a semantic error to reduce the visibility of a member in a derived class. Modern programming languages such as Java and C# refuse to compile such code, because a member that is visible in the base class is always accessible in the derived class through a base pointer.
To add a little to Georg's answer:
Remember that the compiler has no control over and cannot guarantee anything about derived classes. For example, I could ship my type in a library and derive from it in an entirely new program. How is the library compiler supposed to know that derived might have a different access specifier? The derived type didn't exist when the library was compiled.
In order to support this, the compiler would have to know access specifiers at runtime and throw an exception if you attempted to access a private member.