In the grammar of C++ Specification, the members of a class is defined as this:
member-declaration:
decl-specifier-seq(optional) member-declarator-list(optional);
function-definition ;(optional)
::(optional) nested-name-specifier template(optional) unqualified-id ;//what is this?
using-declaration
template-declaration
...
I understand 4 of them. But the 3rd one defines a mandatory nested name specifier followed by an id. e.g
class {
X::Y::z;
}
I don't aware of any C++ syntax that matches this definition. Did I miss something?
The answer is to be found in the [class.access.dcl] section. To put shortly, such a declaration is called 'access declaration' and its purpose is to change access level for an inherited member.
For example:
class A
{
protected:
int a;
int a1;
};
class B
{
public:
int b;
int b1;
};
class C : public A, private B
{
public:
A::a;
B::b;
}
int f()
{
C c;
c.a1; // access error: A::a1 is protected
c.b1; // access error: B is private base of A
c.a; // accessible because A::a is 'published' by C
c.b; // accessible because B::b is 'published' by C
}
This sort of declaration was superseded by using, but kept for compatibility purposes.
Related
In C++ a nested class has access rights to all members of the enclosing class. Does this also apply to a nested class of a nested class?
This code
#include <iostream>
class A
{
public:
class B
{
public:
B() { std::cout << A::x << std::endl; }
class C
{
public:
C() { std::cout << A::x << std::endl; }
};
};
private:
static const int x { 0 };
};
int main()
{
A::B b;
A::B::C c;
}
compiles without warning on g++ 7.2. However, it is unclear to me that this conforms to the standard. The standard draft (N4727 14.7) says:
A nested class is a member and as such has the same access rights as any other member.
However, in the example above C is not a member of A, it is a member of a member. Is the standard ambiguous here? Is g++ behavior portable?
However, in the example above C is not a member of A, it is a member of a member.
Yes this is well-defined behavior; the access right is transfered from B.
According to the standard [class.access]/2,
A member of a class can also access all the names to which the class has access.
And [class.mem]/1,
Members of a class are data members, member functions, nested types, enumerators, and member templates and specializations thereof.
C is a nested class of B, it's also the member of B, then C can access names what B could access to, including A::x. For the same reason, C::C is the member of C, it could access names what C could access to, so accessing A::x in C::C is fine.
The behavior is well-defined and in-line with the standard wording. What you are missing is the relevant wording of [class.access]p2, which strengthens that which you have already quoted:
A member of a class can also access all the names to which the class has
access. A local class of a member function may access the same names that the
member function itself may access.
This means that the accessibility is transitive. If C has access to the same entities as B, it also means that C has access to the entities in A, as B has access to them.
class A {
class B {
class C {
C() { A::x; /* well-defined */ }
};
};
static int x;
};
I recently had to do something like this:
class A { };
class B : private A { };
class C : public B {
public:
A *myA;
};
int main() {
return 0;
}
And I get an error in the three compilers I tried. When I changed the declaration of myA to ::A *myA everything works ok. I poked around in the C++ standard and found Section 11.2, paragraph 3 where it says:
Note: A member of a private base class might be inaccessible as an inherited member name, but accessible directly.
Which is relevant, but unclear. Why is the name A inaccessible? What problems would occur if A was not hidden?
Thanks,
-Ben
Where it could "go wrong":
namespace nmsp
{
class A {};
}
class A {};
class B : private nmsp::A
{
// well-formed:
A* d; // refers to the injected-class-name nmsp::A!!
};
class C : public B
{
// ill-formed
A* p; // refers to the injected-class-name nmsp::A!!
};
It should not depend on the access-specifier in the base-clause whether ::A or nmsp::A is used, otherwise it'd be error-prone IMO.
struct B {};
struct D : private B {
B* fun () { return new D; } // ok
}
struct DD : public D {
B* foo () { return 0; } // error: ‘struct B B::B’ is inaccessible !
};
This error seems unreasonable to me. If we can use simple B* in global scope then why not in its privately derived classes? g++ demo.
We are Not trying to convert DD* to B*, which is forbidden by the language rules (this, this, this are related questions).
Note that, if I change B* foo() to int foo(), things go fine.
So apparently the compiler thinks B is referring to the private constructor of B rather than the type.
Qualifying B apparently fixes that error:
class B* foo () { return 0; }
or this:
::B* foo () { return 0; }
I don't know why that's happening, but maybe this will help.
Update: maybe it's related to 11.2.4 of standard? The only problem is that my standardese isn't good enough to fully understand it.
(sorry for the image, copy/pasting isn't working for me)
A quick look-up for injected-class-name in the Standard yields:
§11.1 [class.access.spec]
5/ [ Note: In a derived class, the lookup of a base class name will find the injected-class-name instead of the name of the base class in the scope in which it was declared. The injected-class-name might be less accessible than the name of the base class in the scope in which it was declared. —end note ]
[ Example:
class A { };
class B : private A { };
class C : public B {
A *p; // error: injected-class-name A is inaccessible
::A *q; // OK
};
—end example ]
I believe this is eerily close to your example ;)
Note clang 3.0's stack, which is slightly more explicit:
$ clang++ -fsyntax-only test.cpp
test.cpp:6:5: error: 'B' is a private member of 'B'
B* foo () { return 0; } // error: ‘struct B B::B’ is inaccessible !
^
test.cpp:2:12: note: constrained by private inheritance here
struct D : private B {
^~~~~~~~~
test.cpp:1:8: note: member is declared here
struct B {};
^
1 error generated.
Here we see that B is accessed through D, instead of directly picked up in the global namespace.
My best guess is that it's not forbidden, C++ simply doesn't see the B type inside that statement or in better terms the label B means nothing.
Some good read about
http://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates/
https://stackoverflow.com/a/7376212/1797612
List of C++ name resolution (and overloading) rules
Consider the following code:
class A
{
private:
class B {};
public:
B f();
};
A a;
A::B g()
{
return a.f();
}
The compiler rejects this - g cannot return A::B because A::B is private.
But suppose I now use decltype to specify the return value of g:
class A
{
private:
class B {};
public:
B f();
};
A a;
decltype(a.f()) g()
{
return a.f();
}
All of a sudden it compiles fine (with g++ >= 4.4).
So I've basically used decltype to get around an access specifier in a way I would not have been able to in C++98.
Is this intentional? Is this good practice?
Access only applies to names (and as a special case, to constructors/destructors). It doesn't apply to entities themselves. The spec further elaborates
[ Note: because access control applies to names, if access control is applied to a typedef name, only the accessibility of the typedef name itself is considered. The accessibility of the entity referred to by the typedef is not considered. For example,
class A {
class B { };
public:
typedef B BB;
};
void f() {
A::BB x; // OK, typedef name A::BB is public
A::B y; // access error, A::B is private
}
— end note ]
So what you have disovered here isn't surprising. You can get hold of the type A::B even in C++03, when taking the address of f by saying &A::f and passing it to a function template deducing the return type.
Here is the example :
struct A
{
A(const int a ):b(a)
{
}
int b;
};
struct B
{
B() : a(5)
{
}
static void A()
{
}
A a;
};
int main()
{
B::A();
}
And the compiler error is :
a9.cpp:19: error: ‘A’ does not name a type
a9.cpp: In constructor ‘B::B()’:
a9.cpp:24: error: class ‘B’ does not have any field named ‘a’
I am using gcc 4.3.0 on fedora 9.
Can someone explains why is the compiler complaining?
If possible, with references from the standard.
Thanks
This works:
struct B {
B() : a(5) { }
static void A() { }
::A a;
};
Since you've used A as a member name in B, that member's definition shadows the A type from the outer namespace. Using :: you can get to that namespace.
This behavior is specified in the (draft) standard as:
3.3.7 (1) "A name can be hidden by an explicit declaration of that same name in a nested declarative region" (the definition of struct B, which is nested in the namespace where struct A is also defined).
Carefully read the introduction to chapter 3, Basic concepts, for further clarification. Especially, this section specifies that
3 (7) Two names are the same if
they are identifiers composed of the same character sequence; or
they are the names of overloaded operator functions formed with the same operator; or
they are the names of user-defined conversion functions formed with the same type.
Note that this last definition does not distinguish between types and class members, so the name hiding (shadowing) rule 3.3.7 (1) applies.