When a lambda function is declared inside a function F which is a friend of class C, does the lambda function have access to C private members? Specifically, does the standard allow it?
C++11 §[expr.prim.lambda] 5.1.2/3:
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. ...
Since the closure type is declared within the friend function, it will have the same access per §[class.local] 9.8/1:
A class can be declared within a function definition; such a class is called a local class. The name of a local class is local to its enclosing scope. The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function. ...
A nested class automatically has access to all the members its "owner" has access to. You don't need lambdas to see this:
class A {
friend struct B;
friend void g();
static void f() { }
};
struct B {
struct C {
static void f() { A::f(); }
};
static void f() { C::f(); }
};
void g() {
struct D {
static void f() { A::f(); }
};
D::f();
}
Despite not being listed explicitly as friends, C::f and D::f can call the private A::f without any complaints from the compiler.
Lambdas are implemented using compiler-generated local classes (that's not just an implementation detail, that's what the standard requires), so the same rules as for other local classes apply.
The rule that local classes can access the same members is spelled out in the standard in 9.8:
The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function.
Related
How is unqualified name lookup being performed for names using within the friend's function body? Let's consider the following code:
#include <iostream>
void foo();
class A
{
friend void foo(){ std::cout << a << std::endl; }
static int a;
};
int A::a = 10;
int main(){ foo(); }
DEMO
The Standard states in the N4296::7.3.1.2/3 [namespace.memdef]:
If a friend declaration in a non-local class first declares a class,
function, class template or function template the friend is a member
of the innermost enclosing namespace.
So, I expected unqualified name lookup didn't find A::a, but it did. I deliberately put the A::a declaration after the friend's function definition in hope it wouldn't be found. What's the actual rule for the friend's unqualified name lookup?
The answer was quite simple:
N4296::3.4.1/8 [basic.lookup.unqual] :
For the members of a class X, a name used in a member function
body, in a default argument, in an exceptionspecification, in the
brace-or-equal-initializer of a non-static data member (9.2), or in
the definition of a class member outside of the definition of X,
following the member’s declarator-id31, shall be declared in one of
the following ways:
[...]
(8.2) — shall be a member of class X or be a member of a base class of
X (10.2),
[...]
N4296::3.4.1/9 [basic.lookup.unqual] :
Name lookup for a name used in the definition of a friend function
(11.3) defined inline in the class granting friendship shall proceed
as described for lookup in member function definitions.
That's it.
UPD:
Inlining is important here. That's why the friend function defined outside the class defintion cannot use static members of the class directly. For instance, the following code pritns compile-time error:
#include <iostream>
class A
{
static int a;
friend void foo();
};
int A::a = 10;
void foo(){ std::cout << a << std::endl; }
int main(){ foo(); }
DEMO
The question is self explanatory, but here's an example if desired:
Say I have a class 'Thing' with a private constructor, that is friends with a function 'make_thing':
class Thing
{
friend std::shared_ptr<Thing> make_thing();
Thing()
{
std::cout << "Thing constructor" << std::endl;
}
};
The 'make_thing' function has a struct defined inside it:
std::shared_ptr<Thing> make_thing()
{
// err: Thing::Thing() is private within make_shared
// auto thing = std::make_shared<Thing>();
// this is okay
struct AccessThing : public Thing {
AccessThing() : Thing() { }
};
auto thing = std::make_shared<AccessThing>();
return thing;
}
This compiles and works in gcc 4.8.2, clang 3.5 and msvc 2013. I can't call
make_shared<Thing>()
because Thing has a private constructor. However, I can get around this by creating a struct (AccessThing) with a public constructor that in turn calls Thing's private constructor, and then pass that to make_shared. To me this makes sense if AccessThing is a friend of Thing.
So what are the rules concerning how friendship is conferred to structs/classes defined within a friend function?
This is the very first paragraph of the standard section on local classes:
A class can be declared within a function definition; such a class is called a local class. The name of a local class is local to its enclosing scope. The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function.
The static keyword is related to internal linkage generally, but the static keyword used inside a class has external linkage right? The variables m, n below are accessible outside the class file.
class c {
int i;
int j;
static int m;
static int n;
public:
void zap();
static void clear();
};
Right.
The keyword static is heavily overloaded with too many different meanings:
On a variable or function at namespace scope it gives the name internal linkage.
On a class member it makes it a static member, which doesn't affect linkage.
On a variable at function scope it gives the variable "static storage duration" as opposed to "automatic" or "dynamic" storage duration (i.e. the variable's lifetime extends to the end of the program, like global variables.)
As I stated in my comment, static members are those associated only with the class rather than individual objects.
static members belong to the class; for variables, they're accessible without an object and shared amongst instances e.g.
struct Foo {
static void *bar;
static void *fu();
}
so Foo::bar and Foo::fu are legal.
They are introduced in §9.4 of the C++03 standard;
A data or function member of a class may be declared static in a class definition, in which case it is a static member of the class.
A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred to using the class member access syntax, in which case the object-expression is evaluated
class process {
public:
static void reschedule();
};
process& g();
void f()
{
process::reschedule(); // OK: no object necessary
g().reschedule(); // g() is called
}
A static member may be referred to directly in the scope of its class or in the scope of a class derived (clause 10) from its class; in this case, the static member is referred to as if a qualified-id expression was used, with the nested-name-specifier of the qualified-id naming the class scope from which the static member is referenced.
int g();
struct X {
static int g();
};
struct Y : X {
static int i;
};
int Y::i = g(); // equivalent to Y::g();
...
You could say that static members are members of the class and not any specific object instance. That is, they have the same value for all object instances.
Static member functions, while not having a value, are otherwise the same. Instead of being unique for each object instance, they can be seen as part of the class. This means that they have no this pointer and can not access non-static member variables.
After searching aroung SO, one question taught me that the lexical scope of an inline friend function is the class it's defined in, meaning it can access e.g. the typedefs in the class without qualifying them. But then I wondered what is the actual scope of such a function? GCC at least rejects all my attempts to call it. Can a function such as in the example ever be called through means other than ADL, which is not possible here thanks to no arguments?
Standard quotations are appreciated, as I currently can't access my copy of it.
The following code
namespace foo{
struct bar{
friend void baz(){}
void call_friend();
};
}
int main(){
foo::baz(); // can't access through enclosing scope of the class
foo::bar::baz(); // can't access through class scope
}
namespace foo{
void bar::call_friend(){
baz(); // can't access through member function
}
}
results in these errors:
prog.cpp: In function ‘int main()’:
prog.cpp:9: error: ‘baz’ is not a member of ‘foo’
prog.cpp:10: error: ‘baz’ is not a member of ‘foo::bar’
prog.cpp: In member function ‘void foo::bar::call_friend()’:
prog.cpp:15: error: ‘baz’ was not declared in this scope
When you declare a friend function with an unqualified id in a class it names a function in the nearest enclosing namespace scope.
If that function hasn't previously been declared then the friend declaration doesn't make that function visible in that scope for normal lookup. It does make the declared function visible to argument-dependent lookup.
This is emphasised in many notes, but the definitive statement is in 7.3.1.2/3 (of ISO/IEC 14882:2011):
Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.
"The C++ Programming Language 3rd Edition (Stroustrap)" : p279:
I. "Like a member declaration, a friend declaration does not introduce a name into an enclosing scope"
II. "A friend class must be previously declared in an enclosing scope or defined in the nonclass
scope immediately enclosing the class that is declaring it a friend"
III. "A friend function can be explicitly declared just like friend classes, or it can be found through its argument types (§8.2.6) as if it was declared in the nonclass
scope immediately enclosing its class."
IV. "It follows that a friend function should either be explicitly declared in an enclosing scope or take an argument of its class. If not, the friend cannot be called. For example:"
//no f() here
void g();
class X{
friend void f(); //useless
friend void g(); //can be found because it is declared outside of class scope
friend void h(const X&); //can be found because the arguments access class members
};
void f() { } //enemy of X :)
But in your case there is more to it that has to do with the namespace, because if you put the proper declaration in foo e.g.:
namespace foo{
struct bar{
friend void baz(const &bar){};
void call_friend();
}
}
does not compile. However, if you declare it outside foo is works like a charm. Now consider that in fact, global, local, struct, and classes are in fact namespaces. Now this leads to the conclusion that the baz(const &) is implicitly defined in the global scope.
This compiles:
namespace foo{
struct bar{
friend void baz(const bar&){};
void call_friend();
};
}
int main(){
foo::bar k;
baz(k);
return 0;
}
Therefore, there are two issues:
The friend declaration does not introduce a name in an enclosing scope, unless IV. Thus the original program cannot find baz() because it has not been properly declared.
If IV , i.e. ADL, then the function is found in foo, but cannot be accessed as foo::baz(k), due to ADL. You will have to explicitely define baz(const bar&) in foo to access it by qualified name.
Thanks, hope it helps, but certainly, I liked the challenge :) .
Interesting!
It seems that the compiler does not know what scope it belongs to (and to be honest there are no clues) and thus puts in in no scope. Some standard digging coming up I suppose.
Note: If you explicitly add a declaration to a particular scope then it starts to work as expected.
namespace foo
{
void baz(); // declare it here and now it works in foo namespace etc.
struct bar
{
friend void baz(){}
void call_friend();
};
}
Digging the standard I find:
11.3 Friends [class.friend]
Paragraph 6
A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope.
[ Example:
class M { friend void f() { } // definition of global f, a friend of M,
// not the definition of a member function
};
— end example ]
Paragraph 7
Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1).
Note:
A free standing function that does not take a parameter is not much use as a friend. As it will have no object on which to take advantage of its friendship (I suppose file scope static storage duration objects).
A friend function defined within an enclosing class, will only be found by argument dependent lookup (ADL). Calling it will succeed when one or more of the arguments is either of the enclosing class type; or of a type declared within the class. Here is an example, displaying "Hello World!", which (for variety) doesn't use an object of the enclosing class type to provide such an argument:
#include <iostream>
struct foo
{
struct local_class{};
friend void greet(local_class o, int) { std::cout << "Hello World!\n"; }
};
int main(int argc, char *argv[])
{
foo::local_class o;
greet(o,1);
return 0;
}
In this Example,
namespace foo{
struct bar{
friend void baz(){}
void call_friend();
};
}
int main(){
foo::baz(); // can't access through enclosing scope of the class
foo::bar::baz(); // can't access through class scope
}
namespace foo{
void bar::call_friend(){
baz(); // can't access through member function
}
}
foo::baz() is inaccessible because the name baz is not visible in scope of namespace foo. If I remember correctly (§ 3.4/2) applies here.
foo::bar::baz() is inaccessible because friends aren't members of class and the scope of inline friend function is namespace or class in which their definition exists therefore, you can't access them outside of that scope.
If you put the declaration of baz() in foo the name of function baz will be visible in foo and the definition will be looked up in the nested scope of bar.
namespace foo{
void baz(); // declaration at namespace scope
struct bar{
friend void baz(){}
};
void call_friend() {
baz(); // ok
}
}
int main()
{
foo::baz(); // ok, bar will be looked up in the nested scope of foo::bar.
}
I think you are confusing friend and private. By declaring function a friend you are granting it access to your private members, and not grating other functions access to it. Anyway, any member function of a struct is accessible by any object because struct members are public by default.
However, in your case baz isn't accessible because by doing friend void baz(){} you didn't really declare the function baz, you just said that it is a friend function. You can just remove the friend keyword, and it will solve all the issues.
We're not allowed to define a functor struct inside a function because one is not allowed to use function declared structs in the instantiation of function templates.
Are there any other significant pitfalls to be aware of? E.g. would this be bad:
int foo()
{
struct Scratch
{
int a, b, c;
};
std::vector<Scratch> workingBuffer;
//Blah Blah
}
1. C++ standard forbids using locally-defined classes with templates.
14.3.1/2: A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.
A code example:
template <class T> class X { /* ... */ };
void f()
{
struct S { /* ... */ };
X<S> x3; // error: local type used as
// template-argument
X<S*> x4; // error: pointer to local type
// used as template-argument
}
Here is a little more reference from IBM documentation:
2. Declarations in a local class can only use type names, enumerations, static variables from the enclosing scope, as well as external variables and functions.
A Code Example:
int x; // global variable
void f() // function definition
{
static int y; // static variable y can be used by
// local class
int x; // auto variable x cannot be used by
// local class
extern int g(); // extern function g can be used by
// local class
class local // local class
{
int g() { return x; } // error, local variable x
// cannot be used by g
int h() { return y; } // valid,static variable y
int k() { return ::x; } // valid, global x
int l() { return g(); } // valid, extern function g
};
}
int main()
{
local* z; // error: the class local is not visible
return 0;
}
3. A local class cannot have static data members
A Code Example:
void f()
{
class local
{
int f(); // error, local class has noninline
// member function
int g() {return 0;} // valid, inline member function
static int a; // error, static is not allowed for
// local class
int b; // valid, nonstatic variable
};
}
The scope of the local classes is the function in which they're defined.But that isn't interesting in itself1.
What makes local classes interesting is that if they implement some interface, then you can create instances of it (using new) and return them, thereby making the implementation accessible through the base class pointer even outside the function.
Some other facts about local classes:
They cannot define static member variables.
They cannot access nonstatic "automatic" local variables of the enclosing function. But they can access the static variables.
They can be used in template functions. They cannot be used as template argument, however.
If they defined inside template function, then they can use the template parameters of the enclosing function.
Local classes are final, that means users outside the function cannot derive from local class to function. Without local classes, you'd have to add an unnamed namespace in separate translation unit.
Local classes are used to create trampoline functions usually known as thunks.
Some references from the Standard (2003)
9.8 Local class declarations [class.local]
\1. A class can be defined within a function definition; such a class is
called a local class. The name of a
local class is local to its enclosing
scope. The local class is in the scope
of the enclosing scope, and has the
same access to names outside the
function as does the enclosing
function. Declarations in a local
class can use only type names, static
variables, extern variables and
functions, and enumerators from the
enclosing scope.
[Example:
int x;
void f()
{
static int s ;
int x;
extern int g();
struct local {
int g() { return x; } // error: x is auto
int h() { return s; } // OK
int k() { return ::x; } // OK
int l() { return g(); } // OK
};
// ...
}
local* p = 0; // error: local not in scope
—end example]
\2. An enclosing function has no special access to members of the local
class; it obeys the usual access rules
(clause 11). Member functions of a
local class shall be defined within
their class definition, if they are
defined at all.
\3. If class X is a local class a nested class Y may be declared in
class X and later defined in the
definition of class X or be later
defined in the same scope as the
definition of class X. A class nested
within a local class is a local class.
\4. A local class shall not have static data members.
Local structs / classes can't have static data members, only static member functions. Also, they can't be templates.
Yes. Local classes can't be used as template parameters in C++03
local structs are perfectly legal, even in C++98. You cannot use them with templates in C++98 though, whereas you can in C++0x. g++ 4.5 supports using local structs with templates in -std=c++0x mode.