visibility of a class data member in a nested class? - c++

AFAIK, data member of enclosing class are also visible in nested class.
struct A {
struct B {
int arr[n]; // how come n is not visible here, but it is visible in next statement.
int x = n;
};
static const int n = 5;
};
see live demo here

how come n is not visible here, but it is visible in next statement.
Because int x = n; is a complete class context while int arr[n]; is not. This can be understood from class.mem which states:
6) A complete-class context of a class is a:
6.4) default member initializer
within the member-specification of the class.
[ Note: A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class.
— end note
]
7) A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.
(emphasis mine)
Note that the n inside int arr[n]; is part of the type of the array but the above quoted statement doesn't allow n to be used as part of a type of a non-static data member and hence the error. For the same reason, we will get the same error if we wrote:
struct A {
struct B {
int x = n;
//----------------------v--------->same error as before
std::array<int, n> arr;
};
static constexpr int n = 5;
};

My answer is more like intuition of why this is so:
In int arr[n] n is used in definition of a filed, while in int x = n n is used to initialize a field. It is like B() : x(n) {}.
Think of the following example:
struct A {
struct B {
int arr[n];
int x = n;
};
static constexpr int n = offsetof(B, x);
};
Here the value of n is dependent on the size of arr and size of the arr is dependent on n. It is a cross dependency. Therefore the struct is not well defined.
If the rules of the C++ was so that your use case be legal, then this should also be legal, which can't be. However, it still allow us to use offsetof in initialization, because why not?

Something about evaluation variable in left-hand side of assignment, not a computer language expert, the following worked.
#include <iostream>
using namespace std;
struct A {
struct B {
int *arr = new int[n]();
int x = n;
};
static const int n = 5;
};
int main() {
cout << "Hello World!";
return 0;
}

Related

C++ is it possible to use a class variable as a default argument

I have a class for a tree as shown below:
class avl
{
node *root;
public:
avl(int data){
root->data = data;
}
int get_height(node *head = root){ //error here
if (head->right == head->left) return 0;
int l = get_height(head->left);
int r = get_height(head->right);
if (l > r) return l+1;
return r+1;
}
}
Unsurprisingly this generates an error at the get_height definition. g++ complains it as an "invalid use of non-static data member". Can I amend this issue or should I resort to an inelegant use of a wrapper here. I'd appreciate if you can add some details to what the standard says about the cause of this error.
Unfortunately this is impossible. Non-static class members can't be used as default arguments.
Non-static class members are not allowed in default arguments (even if
they are not evaluated), except when used to form a pointer-to-member
or in a member access expression.
int b;
class X {
int a;
int mem1(int i = a); // error: non-static member cannot be used
int mem2(int i = b); // OK: lookup finds X::b, the static member
static int b;
};
From the standard, [dcl.fct.default]/9
A non-static member shall not appear in a default argument unless it
appears as the id-expression of a class member access expression
([expr.ref]) or unless it is used to form a pointer to member
([expr.unary.op]). [ Example: The declaration of X​::​mem1() in the
following example is ill-formed because no object is supplied for the
non-static member X​::​a used as an initializer.
int b;
class X {
int a;
int mem1(int i = a); // error: non-static member a used as default argument
int mem2(int i = b); // OK; use X​::​b
static int b;
};
The declaration of X​::​mem2() is meaningful, however, since no object
is needed to access the static member X​::​b. Classes, objects, and
members are described in [class]. — end example ]
As you said you can add an overloaded wrapper function like
int get_height(node *head) {
...
}
int get_height() {
return get_height(this->root);
}

Mutable anonymous struct

I want to define an aggregate with a number of mutable fields (to save it in std::set or std::priority_queue and modify it in future, surely saving the container invariants). I tried the following syntax and it was compiled successfully:
#include <cstdlib>
int
main()
{
struct X
{
mutable struct
{
int i;
int j;
};
};
X const x{};
//x.i = 1;
return EXIT_SUCCESS;
}
Live example for clang 3.8.
But statement x.i = 1; gives an error:
error: cannot assign to variable 'x' with const-qualified type 'const X'
My intention was to group a plenty of sequential fileds and apply mutable keyword to the group.
Is this syntax wrong? If so, what is an intention of compiler makers to allow such a syntax if any?
ADDITIONAL:
Code:
#include <cstdlib>
int
main()
{
struct X
{
mutable struct
{
int i;
int j;
};
void f() const { i = 1; }
};
X const x{};
//x.i = 1;
x.f();
return EXIT_SUCCESS;
}
gives an error too:
error: cannot assign to non-static data member within const member function 'f'
note: member function 'main()::X::f' is declared const here
void f() const { i = 1; }
The trouble comes from the mix between the non-standard (in C++) usage of anonymous struct together with the mutable. The latter is a storage class specifier that is meant to be used for members and not for types.
Alternative 1: define an intermediate member for your anonymous struct:
You can define a member that will then be mutable, according to the rules of standard C++:
struct X
{
mutable struct
{
int i;
int j;
} y;
};
X const x{};
x.y.i = 1;
Live demo
Alternative 2: make every members in the anonymous struct mutable:
You can otherwise define the members within the struct as being mutable. As the anonymous struct merges these members into the enclosing struct, the mutable property will be passed on:
struct X
{
struct
{
mutable int i;
mutable int j;
};
};
Online demo
What does the standard say ?
The standatad C++ doen't allow anonymous struct. Anonymous struct is a compiler extension for C11 compatibility.
The C++ standard allows however anonymous unions. But it sets restrictions, notably:
9.5/6: A storage class is not allowed in a declaration of an anonymous union in a class scope.
So when compiling the following code:
struct X
{
mutable union
{
int i;
int j;
};
};
the compiler shall and will issue a very specific error:
prog.cpp:11:13: error: a storage class on an anonymous aggregate in class scope is not allowed
mutable union
I think that it is not consistent to allow using a storage class specifier on an anonymous struct (and apparently ignoring it) and to issues an error for an anonymous union. According to me, this shall be reported as a compiler bug. In anycase, you should adopt alternative 1 (portable & compliant) or alternative 2 (compiler dependent, but more consistent with the standard).

static const in c++ class: undefined reference

I have a class for local use only (i.e., its cope is only the c++ file it is defined in)
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
All the code reside in the same c++ file. When compiling using VS on windows, there is no problem at all.
However, when compiling on Linux I get the undefined reference error only for the second statement.
Any suggestions?
std::min<int>'s arguments are both const int&(not just int), i.e. references to int. And you can't pass a reference to A::MY_CONST because it is not defined (only declared).
Provide a definition in the .cpp file, outside the class:
class A {
public:
static const int MY_CONST = 5; // declaration
};
const int A::MY_CONST; // definition (no value needed)
// initialize static constants outside the class
class A {
public:
static const int MY_CONST;
};
const int A::MY_CONST = 5;
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
To explain what's happening here:
You declared static const integer inside class, this "feature" is here to be able to use it as constant expression,i.e. for local array size, template non-type parameters, etc.. If compiler wants to use this constant expression it must be able to see it's value in that translation unit.
9.5/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.
odr-used means to form reference to that variable or take it's address.
std::min takes it's parameters by reference, so they are odr-used.
Solution:
Define it!
class A
{
static const int a = 5;
};
const int A::a; //definition, shall not contain initializer
You can also save the const value to a local variable.
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error: undefined reference to `A::MY_CONST`
int l = std::min<int>( j, b); // works
}
I am having a very strange situation
template<class T> class Strange {
public:
static const char gapchar='-';
};
template<class T> void Strange<T> method1 {
char tmp = gapchar;
}
template<class T> void Strange<T> method2 {
char tmp = gapchar;
}
I include the above class, it has been working for several years.
I added another method, essentially the same signature and just reading the gapchar.
I got undefined error only for the third method, even I am using all three methods.
Then I changed the way I initialize the static variable by
not initializing in the class definition:
static const char gapchar;
template<class T> const char Strange<T>::gapchar='-';
This solved the problem. I could not figure out why the old way of
initializing int or char type (the only two types allowed) inside the class
definition section stop working for only one of the methods but not others.
If you are using some header-only thing and want to avoid having to add a .cpp file, it seems like you can do this:
class A {
public:
static inline const int MY_CONST = 5;
};
(or `static inline constexpr`)
This requires C++17

Namespace scope vs scope inside class

There is this code:
namespace N {
struct B {
void f() {
i;
j;
}
int i;
};
int j;
}
int main() {
return 0;
}
Variable i is found but variable j is not. How does it work that variable in the class can be used before it is declared but the same does not work for namespace? How i is found - compiler parses first all class to find all members then bind it with references from member functions?
Indeed, the bodies of member functions (as well as variable initializers) are processed in a later phase than the class definition. You can check for yourself that the "declare before use" rule is still in full effect for class members, by trying to use members in other member declarations:
struct B
{
char c[sizeof i];
int i;
};
Demonstration: http://ideone.com/v1ksio
struct B2
{
decltype(i) f();
int i;
};
This also affects usage of the class itself which requires a complete type.
struct B
{
static char c[sizeof (B)];
};
Demonstration: http://ideone.com/Z9XOzm
But this is ok, because variable initializers are processed when the construction definitions are, where all members are known and the type is complete:
struct B
{
unsigned r{sizeof s};
unsigned s{sizeof (B)};
};

Use of variables in global namespace

Why should this be an error?
int a = 0;
a = 42;
int main()
{
}
A possibe match for this behavior i could find:
(3.4.1/4) A name used in global scope, outside of any function, class
or user-declared namespace, shall be declared before its use in global
scope.
Could this be a defect in standard?
int a = 0; //it is a declaration (and definition too) statement
a = 42; //it is an assignment statement
The second line is the cause of error, for it is an assignment statement.
At the namespace-level, only declaration and definition statements are allowed. Assignment-statements are not allowed at namespace level.
And by "shall be declared before its use in global scope" (from the spec's quotation) means the following:
int a = 42;
int b = 2 * a; //a is being used here
int c = a + b; //both a and b are being used here
If you define type instead, then:
struct A {}; //definition of A
struct B { A a; }; //a is declared as member of B
//(which means, A is being "used")
void f(const A&, const B&); //both A and B are used in the declaration of f
You cannot write an assignment statement like that in the global namespace
it needs to be either in main or in some [member] function
int main()
{
a=42;
return 0;
}