I'm reading C++11 Standard (N3092).
11-4 Member access control says
Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. [ Note: access control applies to names nominated by friend declarations (11.4) and using- declarations (7.3.3). — end note ]
11.4-9 Friends says
A name nominated by a friend declaration shall be accessible in the scope of the class containing the friend declaration. The meaning of the friend declaration is the same whether the friend declaration appears in the private, protected or public (9.2) portion of the class member-specification.
With my poor English skill, it seems these two excerpts aren't consistent. What if an access control is applied to friends, as indicated in the first excerpt? Could anyone please give me a concrete example code?
The first excerpt also says about using-declarations. That can be confirmed via the code below. So you can say "access control is certainly applied to using-declarations." But I have no idea how to write a code to see the behavior of access-controled friend functions.
#include <iostream>
using std::cout;
class B {
public:
void f() { cout << "B::f()\n"; }
void f(int) { cout << "B::f(int)\n"; }
};
class D : public B {
public:
using B::f; //`using` declaration in `public` context
void f() { cout << "D::f()\n"; }
};
class D2 : public B {
using B::f; //`using` declaration in `private` context
public:
void f() { cout << "D2::f()\n"; }
};
int main() {
D d;
d.f(); //=> "D::f()"
d.f(0); //=> "B::f(int)"
D2 d2;
d2.f(); //=> "D2::f()"
d2.f(0); //=> "error: ‘void B::f(int)’ is inaccessible within this context"
}
As the code above, if I write
public:
friend void some_func() { }
, is access control applied to the name some_func? How?
it seems these two excerpts aren't consistent
They are consistent, but they don't have to be. Anything inside a [note: ] block is non-normative text. It is there to summarize something for human purposes or to provide clarifying examples, but it is not used to define the actual behavior of the language. So the only excerpt that actually matters is the second one.
The bolded text in first excerpt is talking about the content of the friend declaration. If you have two classes, A and B, and you want to make a specific member of B a friend of A, what the bolded text is saying is that the specific member you name must already be accessible by A. That is, this is illegal:
class B
{
private:
void SomeMember();
};
class A
{
private:
friend void B::SomeMember(); //`SomeMember` is not accessible to `A`, so ill-formed.
};
B would have to make itself a friend of A, so that A could name its private members.
The bolded text in the second excerpt merely says that the location where the friend declaration happens in a class doesn't matter. It doesn't matter if the friend declaration is public, private, or whatever. That is, all of the following mean the same thing:
class B;
class A1
{
public:
friend class B;
};
class A2
{
protected:
friend class B;
};
class A3
{
private:
friend class B;
};
So the two bolded excerpts have nothing to do with one another.
Now, the unbolded part of the second except actually says in normative language what the notation in the first excerpt says. Namely, names specified in a friend declaration have to be accessible.
if I write <...> is access control applied to the name some_func? How?
It's fine. friend definitions always define non-member functions. As such, they're not in the scope of a class, so they are de-facto public. And therefore accessible.
Related
class B;
class A {
private:
int numA;
public:
A(): numA(12) { }
// friend function declaration
friend int add(A, B);
};
this does not give any error on declaring object of class B in friend
function,,but this gives,,as firstly class B is declared
class Apple;
class B {
private:
int b;
public:
void showA(Apple d)
{
// Since B is friend of A, it can access
// private members of A
cout << "A::a=" ;
}
};
};
this gives an error of incomplete type for object d,,why this is happening though we already declared class apple before,
Why does the first example compile?
In your first example, you have a forward declaration of class B followed by a declaration of a friend function that uses it in its parameter list:
class B;
class A {
...
friend int add(A, B);
};
This is allowed because, although B is incomplete, we are not defining add yet, only declaring an intention to eventually do so.
Why does the second example not compile?
In the second example, we have a forward declaration of class Apple, followed by a definition of showA:
class Apple;
class B {
...
void showA(Apple d)
{
...
}
};
This time, since we are defining the function, the compiler is obligated to generate code for it. But because Apple is incomplete, the compiler cannot know, for example, how much space in memory to reserve to hold the parameter d. Therefore this is an error.
The question When can I use a forward declaration? explains some of what can and cannot be done with an incomplete (forward-declared) type.
The use of 'friend' is irrelevant here
The friend keyword is basically irrelevant here. friend primarily affects access control (in the sense of public and private), but that's not the issue here.
A detail: friend also affects scoping. Because of friend, add is not a member of class A, but rather refers to a member of the global scope without actually introducing one (it's weird). But that does not change whether an incomplete type can be used as a parameter.
I'm trying to compile a program which was written with super modern arcane coding techniques. These techniques are so advanced that GCC and Clang work but Visual Studio 2017 throws an error. Now I'm wondering whether Visual Studio got it right.
Consider the following program:
#include <functional>
#include <iostream>
class A
{
public:
A(int i) : foo(i) { }
private:
int foo;
friend class B;
};
class B
{
public:
void printFooFromA(const A& a, std::function<void (const A&)> printer = [](const A& a) {
std::cout << "a.foo is " << a.foo << std::endl;
}) {
printer(a);
}
};
int main()
{
A a(123);
B b;
b.printFooFromA(a);
return 0;
}
Visual Studio throws Error C2248:'A::foo': cannot access private member declared in class 'A'.
The root of the error is in the declaration for printFooFromA: The "printer" argument is given a default value in the form of a lambda. Inside the lambda, it accesses A::foo. Since foo is private, it can only be accessed within A or a friend of A.
Whether this is an error or not hinges on if the lambda should be considered a friend of A. Visual Studio says no while GCC says yes. Does the C++ standard specify this?
Edited to add: There are existing questions on StackOverflow concerning whether a lambda is considered a friend of a class, but none of those questions address the case where a lambda is in default argument position and whether this complies with standard C++.
According to C++17 [class.friend]/2:
Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in the base-specifiers and member declarations of the befriended class.
Declaring a class member function is certainly a member declaration, and the lambda is certainly in the declaration, so I would interpret this as saying that the lambda has friendship status.
To back this up, there is [expr.prim.lambda.closure]/2:
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [Note: This determines the set of namespaces and classes associated with the closure type.]
So the closure type of the lambda is declared in B's scope, meaning that B is roughly equivalent to:
class B
{
struct lam
{
void operator()(const A& a)
{
std::cout << "a.foo is " << a.foo << std::endl;
}
};
public:
void printFooFromA(const A& a, std::function<void (const A&)> printer = lam())
{
printer(a);
}
};
In [class.friend]/2 it gives an example with this same layout, showing that friend class X; means that nested classes of X are also friends.
It seems clear that this is therefore a bug in MSVC.
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;
};
Sometimes, C++'s notion of privacy just baffles me :-)
class Foo
{
struct Bar;
Bar* p;
public:
Bar* operator->() const
{
return p;
}
};
struct Foo::Bar
{
void baz()
{
std::cout << "inside baz\n";
}
};
int main()
{
Foo::Bar b; // error: 'struct Foo::Bar' is private within this context
Foo f;
f->baz(); // fine
}
Since Foo::Bar is private, I cannot declare b in main. Yet I can call methods from Foo::Bar just fine. Why the hell is this allowed? Was that an accident or by design?
Oh wait, it gets better:
Foo f;
auto x = f.operator->(); // :-)
x->baz();
Even though I am not allowed to name the type Foo::Bar, it works just fine with auto...
Noah wrote:
type names defined within a class definition cannot be used outside their class without qualification.
Just for fun, here is how you can get at the type from outside:
#include <type_traits>
const Foo some_foo();
typedef typename std::remove_pointer<decltype( some_foo().operator->() )>::type Foo_Bar;
Trying to find anything in the standard that would spell it out in detail but I can't. The only thing I can find is 9.9:
Type names obey exactly the same scope rules as other names. In particular, type names defined within a class definition cannot be used outside their class without qualification.
Essentially, the name of Foo::Bar is private to Foo, not the definition. Thus you can use Bars outside of Foo, you just can't refer to them by type since that name is private.
The name lookup rules for members would also seem to have some effect on this. I don't see anything that specifically references "nested class" and thus they wouldn't be allowed to (if my lack of finding anything in fact is because it's not there).
I can't provide a full answer, but maybe a starting point. The C++ 1998 specification includes the following code example under paragraph 11.3 [class.access] (p. 175):
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
}
In this example, a private type is "published" through a public typedef. Although it's not the same thing as publishing a type through a member function signature, it's similar.
I think this is by design. You cannot explicitly create instance of Foo::Bar but it could be returned from member functions and then you could pass it to other member functions. This lets you to hide implementation details of your class.
In C++, will a member function of a base class be overridden by its derived class function of the same name, even if its prototype (parameters' count, type and constness) is different? I guess this a silly question, since many websites says that the function prototype should be the same for that to happen; but why doesn't the below code compile? It's a very simple case of inheritance, I believe.
#include <iostream>
using std::cout;
using std::endl;
class A {};
class B {};
class X
{
public:
void spray(A&)
{
cout << "Class A" << endl;
}
};
class Y : public X
{
public:
void spray(B&)
{
cout << "Class B" << endl;
}
};
int main()
{
A a;
B b;
Y y;
y.spray(a);
y.spray(b);
return 0;
}
GCC throws
error: no matching function for call to `Y::spray(A&)'
note: candidates are: void Y::spray(B&)
The term used to describe this is "hiding", rather than "overriding". A member of a derived class will, by default, make any members of base classes with the same name inaccessible, whether or not they have the same signature. If you want to access the base class members, you can pull them into the derived class with a using declaration. In this case, add the following to class Y:
using X::spray;
That's so called 'hiding': Y::spray hides X::spray.
Add using directive:
class Y : public X
{
public:
using X::spray;
// ...
};
Classes are scopes and a class scope is nested in its parent. You have exactly the same behavior with other nested scopes (namespaces, blocks).
What happen is that when the name lookup searches for the definition of a name, it looks in the current namespace, then in the englobing namespace and so on until it find one definition; the search then stop (that's without taking into account the complications introduced by argument dependent name lookup -- the part of the rules which allows to use a function defined in the namespace of one of its argument).