My question is about lambda scope for the static member initializers. Consider the following test:
#include <functional>
#include <iostream>
struct S {
static const std::function<void(void)> s_func;
};
const std::function<void(void)> S::s_func = []() {
std::cout << "Hello from " << __PRETTY_FUNCTION__ << std::endl;
};
int main(void) {
S::s_func();
return 0;
}
gcc starting from 4.8 defines the lambda in the scope of S, so the program outputs something like this:
Hello from S::<lambda()>
(gcc-4.8.2 has a different definition for __FUNCTION__ & Co macros, but nevertheless the lambda is still defined within S)
Meanwhile gcc-4.7 defines the lambda in the global scope, so the program outputs
Hello from <lambda()>
Probably newer gcc are more standard-compliant. However I'd like to ask if the standard actually specifies this aspect or it can be implementation-dependent.
Update: as #user5434961 suggested that all __FUNCTION__-alike macros are implementation dependent, so it's better to avoid them in a standard-compliant test. So here is the example which can be compiled if a compiler defines such lambdas within S scope and breaks the compilation otherwise:
#include <functional>
#include <iostream>
struct S {
static const std::function<void(void)> s_func;
private:
static const int s_field;
};
const std::function<void(void)> S::s_func = []() {
std::cout << "Hello from S::s_func. S::s_field = " << S::s_field << std::endl;
};
const int S::s_field = 1;
int main(void) {
S::s_func();
return 0;
}
This issue has been raised before but I cannot find the relevant bug report. Here's a broken link for a MSVC bug report that was supposedly filed (it's still not fixed in 2015: you can test it at rise4fun). It has however been fixed somewhere between 4.7 and 4.8 for GCC. The relevant standardese used to back this up as a bug is:
[C++11, 9.4.2/2] The initializer expression in the definition of a
static data member is in the scope of its class.
[C++11, 5.1.2/2] The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5).
[C++11, 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.
Previously
Why lambda in static initializer can't access private members of class in VC++2013?
C++11 lambdas can access my private members. Why?
Why is it not possible to use private method in a lambda?
I guess it should be in the class scope. Quoted from cppreference (emphasis mine):
The lambda expression constructs an unnamed prvalue temporary object
of unique unnamed non-union non-aggregate type, known as closure type,
which is declared (for the purposes of ADL) in the smallest block
scope, class scope, or namespace scope that contains the lambda
expression.
In the out-of-line definition of S::s_func, you enter into the scope of S the time S:: is encountered. So, the lambda expression is contained in the class scope of S. As the closer type is a member of S, access to private members of S is granted.
Related
Consider the following code, provided by a colleague:
#include <array>
#include <string>
int main() {
const int size = 4;
return [size]() {
std::array<std::string, size> a; // *
return a.size();
}();
}
It's accepted by Clang 5.0.0 but rejected by GCC 7.2 with the error message for the starred line being:
error: '__closure' is not a constant expression
Which compiler is right?
The rule is actually intuitive: any occurrence of a variable that doesn't necessitate a capture refers to the original variable. [expr.prim.lambda]/11:
Every id-expression within the compound-statement of a
lambda-expression that is an odr-use of an entity captured by copy is
transformed into an access to the corresponding unnamed data member of
the closure type. [ Note: An id-expression that is not an odr-use
refers to the original entity, never to a member of the closure type.
[…] — end note ]
Clearly, the declared size variable can be used in constant expressions, hence Clang is right.
Consider this piece of code:
class shy {
private:
int dont_touch; // Private member
public:
static const shy object;
};
const shy shy::object = []{
shy obj;
obj.dont_touch = 42; // Accessing a private member; compiles; WHY?
return obj;
}();
int main()
{
}
Live code (Clang)
Live code (GCC)
It seems really unintuitive to me. What does the C++11/14 standard say about this? Is this a GCC/Clang bug?
As already answered in the comments #Tony D and #dyp:
§ 9.4.2/2 Static data members [class.static.data]:
The initializer expression in the
definition of a static data member is in the scope of its class.
The above means that static data members and their initializers can access other private and protected members of their class.
Also consdering the standard § 5.1.2/2&3 Lambda expressions [expr.prim.lambda]:
2 The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A closure object behaves like a function object (20.9).-end note]
3 The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion 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.
Combining the above we end up to the conclusion that the prvalue temporary closure object of your lambda is declared and defined in the initializer of the static const data member shy::object and consequently the scope of the lambda's closure object is the scope of class shy. As such it can access private members of class shy.
Given the following sample code:
int main()
{
int i;
auto f = [=]()mutable->int*
{
return &i;
};
return 0;
}
g++ v.4.8.1 warns that "address of local variable ‘i’ returned".
Clang v.3.2 (MacOS's Clang) warns that "address of stack memory
associated with local variable 'i' returned".
Neither VS2012 nor VS2013 RC warn of anything.
My understanding of lambdas is that the compiler will generate a functor class. That functor class will have members for all copied variables (i in the example). I believe that in the context of my code, as long as f exists, it is safe to return the address of one of its members. It seems to me that all compilers got it wrong. I think a warning about using the address of f's member i after f goes out of scope is valid, but the warnings about the "local variable 'i'" are incorrect/misleading. Am I right?
Yes, you're right. The & operator is applying to the member, not the local object.
Demonstration is straightforward: just modify your example to output the addresses.
#include <iostream>
int main() {
int i;
std::cout << & i << '\n';
std::cout << [=]() mutable -> int * {
return & i;
} () << '\n';
}
http://ideone.com/OqsDyg
Incidentally, this compiles with no warnings under -Wall in GCC 4.9.
Some terminology:
The = or & inside [&](){ /*..*/ } is called a capture-default.
odr-use of a variable roughly means that the variable does not appear in a discarded-value-expression (such as (void)some_variable or int x = some_variable, 5;) and it doesn't occur in a constant expression.
compound-statement is the "function block" { statements }
the name of a variable is an id-expression
[expr.prim.lambda]/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.
/11
If a lambda-expression has an associated capture-default and its compound-statement odr-uses (3.2) this or a variable with automatic storage duration and the odr-used entity is not explicitly captured, then the odr-used entity is said to be implicitly captured;
Therefore, i is implicitly captured.
/14
An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non-static data member is declared in the closure type.
There is a non-static data member (of type int) in the closure type.
/17
Every id-expression that is an odr-use (3.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.
We don't even need to interpret this, as this paragraph provides us with an example very similar to the OP's:
void f(const int*);
void g() {
const int N = 10;
[=] {
int arr[N]; // OK: not an odr-use, refers to automatic variable
f(&N); // OK: causes N to be captured; &N points to the
// corresponding member of the closure type
};
}
If we apply this to the OP's example, we see that &i refers to the internal non-static data member of the closure type. Whether or not the diagnostic message is appropriate is not specified in the Standard ;)
This code does't compile, and the error information is "undefined reference to `A::a'":
code 1:
#include <iostream>
using namespace std;
class A
{
public:
static const int a=0;
};
int main()
{
cout<<&A::a<<endl;
return 0;
}
But for a non-const static member it compiles:
code 2:
#include <iostream>
using namespace std;
class A
{
public:
static int a;
};
int A::a=0;
int main()
{
cout<<&A::a<<endl;
return 0;
}
Is there just no way of accessing the address of a static const member of a class? If there is, how? And why code 1 does not compile?
Put
const int A::a;
In the source file, otherwise the compiler doesn't generate an address for a. Note the value is not repeated here.
This code does't compile
The code does not link, it does compile.
The static const member requires a definition if its address is to be used, so just add a definition similar to the second code snippet:
const int A::a;
Taking the address of A::a means A::a is odr-used, and from section 9.4.2 Static data members of the C++11 standard (draft n3337), clause 3:
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class
definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression
is a constant expression (5.19). A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. —end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
Since you want to take the address, you still need to define the static member, as you do for the non-const version where you say
int A::a;
For the const version you also need
const int A::a;
See here for a good discussion - headline quote
"compile-time constants don't have addresses."
I know that the C++ standard says (sec 9.4.2 paragraph 4) that a static member variable of integral or enum type can provide an initializer inside the class, but that this requires a definition of that member outside the class (in a compilation unit). I.e., you need to do something like this:
class A
{
public:
static const int X = 10;
};
// this is required by the standard
const int A::X;
I've seen (and I've seen it said other places) that some compilers will let you get away without the outside-of-class definition. This works on gcc 4.2.1 on OS X:
#include <iostream>
class A
{
public:
static const int X = 10;
};
int main(int argc, char** argv)
{
std::cout << A::X << std::endl;
return 0;
}
I recently encountered a bug where someone had done this, but they were using the member variable inside a templated function (std::max to be exact), and it would NOT compile, complaining about the undefined symbol A::X. I.e., this doesn't work:
#include <iostream>
#include <algorithm>
class A
{
public:
static const int X = 10;
};
int main(int argc, char** argv)
{
std::cout << std::max(1, A::X) << std::endl;
return 0;
}
Adding back in the outside-of-class definition makes it work.
This is sort of an academic question, but I'd like to know why this happens. Especially in relation to the fact that if we replace the static member variable with a static function (static const int X = 10; becomes static int X(), A::X becomes A::X()), then it will compile without the outside-of-class definition. The reason I mention templates is because std::max is templated, and other templated functions reproduce the same behavior. It may not be specifically related to templates, but I'd like to understand why it is that templates cause the behavior that they do. I assume this must have to do with the way that templates and static members get compiled/implemented?
PS - I posted some minimal code on github
It will compile without a definition.
The definition is needed at link time, if the static member variable is odr-used. (It wouldn't be odr-used if the compiler managed to substitute its actual value every time it was referenced. On the other hand, taking its address is sure to make it odr-used)
This is the complete rule (section 9.4.2 [class.static.data]):
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
and from section 3.2 [basic.def.odr]:
A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an
object that satisfies the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied.
The requirements for appearing in a constant expression ARE satisfied, so everything depends on whether it is used as an lvalue or rvalue.
std::max takes an lvalue reference, so there is not an immediate lvalue-to-rvalue conversion.
The only interaction with templates is that there could be multiple equivalent definitions and the linker will pick any one. In your case, there is no template, so multiple definitions would produce a "symbol multiply defined" type of error.
When you forget to provide a definition, both cases (member of template class and member of ordinary class) will give the same error: "undefined symbol".
Let's take your first example:
std::cout << A::X << std::endl;
Because A::X is of type const int, this calls cout.operator<<(int value). Notice that in this call int value is taken by value. I believe the compiler performs constant folding and just replaces the A::X with the value. However, it is not required to do this in C++03. However, in C++11 the rules have changed and this has become required by this quote: unless it is an object that satisfies the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied. (As mentioned by Ben Voigt).
Now let's look at the definition for std::max(1, A::X):
template< class T >
const T& max( const T& a, const T& b );
The std::max function takes it's arguments by reference, in other words, the address of A::X has to be known in order to complete the call. The address of A::x must be known which requires a definition.