Is it possible to refer to class members inside "in class initializers"?
Example:
struct Example
{
std::string a = "Hello";
std::string b = a + "World";
};
It seems to work (compiles and runs) but is it ok to do?
This is allowed in default initializers since C++11. Scroll down to the "Usage" section and look at the first example. I copied the explanation and example here for easier reference:
The name of a non-static data member or a non-static member function can only appear in the following three situations:
As a part of class member access expression, in which the class either has this member or is derived from a class that has this member, including the implicit this-> member access expressions that appear when a non-static member name is used in any of the contexts where this is allowed (inside member function bodies, in member initializer lists, in the in-class default member initializers).
struct S
{
int m;
int n;
int x = m; // OK: implicit this-> allowed in default initializers (C++11)
S(int i) : m(i), n(m) // OK: implicit this-> allowed in member initializer lists
{
this->f(); // explicit member access expression
f(); // implicit this-> allowed in member function bodies
}
void f();
};
#include<bits/stdc++.h>
using namespace std;
struct Example
{
string a = "Hello";
string b = a + "World";
};
int main(){
Example val ;
val.a = "Salom";
cout<<val.a<<" "<<val.b;
}
it is okey, you can do it .
but captures the initial result you have If you give a a new value b will not be updated
Related
Someone gave me (part of) the following code:
struct MyStruct
{
int x = {};
int y = {};
};
I never saw this syntax before, what does initialization with {} mean?
This is default member initializer (since C++11),
Through a default member initializer, which is a brace or equals initializer included in the member declaration and is used if the member is omitted from the member initializer list of a constructor.
The initialization itself is copy-list-initialization (since C++11), as the effect, the data member x and y would be value-initialized (and zero-initialized as built-in type) to 0.
Since the C++11 standard there are two ways to initialize member variables:
Using the constructor initialization list as "usual":
struct Foo
{
int x;
Foo()
: x(0)
{
}
};
Use the new inline initialization where members are getting their "default" values using normal initialization syntax:
struct Foo
{
int x = 0;
};
Both these ways are for many values and types equivalent.
This question already has an answer here:
Why can in-class initializers only use = or {}? [duplicate]
(1 answer)
Closed 4 years ago.
I am sure that I misunderstood something.
After instantiate a struct object inside class and pass it as value in constructor I get an error?
error: 'test' is not a type
#include <iostream>
using namespace std;
struct Test
{
int x = 11;
int y = 22;
};
class A
{
private:
int foo = 100;
public:
A(Test tmp){}
};
class B
{
private:
Test test;
A a(test); //error
public:
B(){}
};
int main()
{
B b;
return 0;
}
If you want to declare a as a data member and initialize it, the correct syntax should be:
class B
{
private:
Test test;
A a{test}; // default member initializer
A a = test; // default member initializer
A a = A(test); // default member initializer
public:
B() : a(test) {} // member initializer list
};
Note that default member initializer only support brace or equals but not parentheses initializer. And it is used only if the member is omitted in the member initializer list.
The compiler is trying to interpret a as a function, which returns A and taking test as parameter. test is not a type name then compiling fails.
After [I] instantiate a struct object inside class...
class B
{
private:
Test test;
This is not instantiating anything. This declares that every (at this point, theoretical object of type B will have a member variable named test, of type Test.
(Besides, get into the habit of making any member variable private, not public, as early on in your learning process as possible. A class is not just a struct with functions, it is an elementary OOP building block that should not expose its data members to public -- doing so is usually a design error.)
A a(test); //error
This is not "passing [test] as parameter to a constructor", as you stated!
This is declaring a member function to class B named a, which returns an object of type A, and takes an unnamed parameter of type test.... which, as there is no such type, just a member variable of that name, this an error.
As the overall purpose of your code example is rather unclear, it is difficult to say what would be "correct" for you.
Consider the following example:
class A { int x; };
Now what is A::x?
It cannot be an lvalue because it does not refer to a storage Location.
It cannot be a type, because the type would be decltype(A::x).
It is, in fact, an lvalue. [expr.prim.id.qual]/2:
A nested-name-specifier that denotes a class [...] followed by the
name of a member of either that class ([class.mem]) or one of its base
classes, is a qualified-id [...]. The result is the member. The type
of the result is the type of the member. The result is an lvalue if
the member is a static member function or a data member and a prvalue
otherwise.
Though its usage outside a class member access expression is severely restricted by [expr.prim.id]/2, it can notably be used in unevaluated operands, where its lvalueness can manifest:
struct A {
int x;
};
void f(int &);
using p = decltype(&(A::x)); // p is int*; the parens prevents forming a pointer-to-member
using q = decltype(f(A::x)); // q is void
A::x is merely a less ambiguous way of referring to the member x which can be necessary if the local context shadows that member:
Example 1:
A child class also has a member x.
struct B : public A
{
int x;
void foo()
{
int local_x = x;
int parent_x = A::x;
}
}
which would compile had you made x protected in class A (it's currently private).
Example 2:
You can even use it in a member function of A that has x as a parameter:
class A {
int x;
void foo(int x)
{
int parameter_x = x;
int member_x = A::x;
}
};
In addition to Bathsheba's answer, which is correct when you're within the class's scope, A::x can also be used as part of a pointer-to-member expression, i.e. &A::x, which returns a int A::*.
In this case, the standalone A::x is not valid and therefore the question of what it returns is moot.
From the C++ Standard (5.1 Primary expressions)
9 A nested-name-specifier that denotes a class, optionally followed by
the keyword template (14.2), and then followed by the name of a member
of either that class (9.2) or one of its base classes (Clause 10), is
a qualified-id; 3.4.3.1 describes name lookup for class members that
appear in qualified-ids. The result is the member. The type of the
result is the type of the member. The result is an lvalue if the
member is a static member function or a data member and a prvalue
otherwise. [ Note: a class member can be referred to using a
qualified-id at any point in its potential scope (3.3.7). —end note
]...
The qualified name allows to specify an otherwise hidden name for example when derived and base classes have members with the same name. For example
#include <iostream>
struct A
{
int i;
};
struct B : A
{
B( int i ) : A { i }, i( 2 * A::i ) {}
int i;
std::ostream &out( std::ostream &os = std::cout ) const
{
return os << "A::i = " << A::i << ", B::i = " << B::i;
}
};
int main()
{
B( 10 ).out() << std::endl;
return 0;
}
The program output is
A::i = 10, B::i = 20
Also (5.1 Primary expressions)
13 An id-expression that denotes a non-static data member or
non-static member function of a class can only be used:
(13.1) — as part of a class member access (5.2.5) in which the object
expression refers to the member’s class63 or a class derived from that
class, or
(13.2) — to form a pointer to member (5.3.1), or
(13.3) — if that id-expression denotes a non-static data member and it
appears in an unevaluated operand.
A::x is exact equivalent of accessing static variable in JAVA.
Regarding your example:-
1) x will be variable for distinct objects
2) A::x will be the variable for the class itself. Regardless of how many objects of A has been declared for every object the value of A::x will be same until assignment.
For example:-
#include<iostream>
using namespace std;
class A {
public:
static int x;
};
int A::x;
int main()
{
A a,b;
a.x=8;
b.x=6;
A::x=10;
return 0;
}
I mean, we can't do:
class A {
int i;
char c;
public:
A(int i = 0, char c = ' ') : this->i(i), this->c(c) {}
};
Is it because the instance hasn't been created yet or something like that?
You don't need the this keyword in the initializer list as anything in the initializer list must be declared within the class. When you do
class MyClass
{
int member;
public:
MyClass(int member) : member(member) { }
^^^^^ Can only be resolved to a member of MyClass
}
member in the initializer list is not permitted to refer to anything other than int member within the class. Therefore the this keyword is unnecessary and would add additional parsing difficulties if compilers had to support both member(member) and this->member(member) within initializer list. While giving the impression that it is possible to initialize non-member variables within the initializer list at the same time.
If you look at an overview of the C++ grammar, specifically the mem-initializer-list section, you see that the initializer list should consist of identifier(expression) or classname(expression) entries. An identifier is defined here as string of digits and nondigits (a-z, A-Z, _).
Your this->identifier(expression) doesn't match that, so it's not allowed. It's not necessary anyway, because there is no ambiguity here. The identifier is always a member variable of the class, so it's clear which i is referred to.
class A {
int i;
char c;
public:
A(int i = 0, char c = ' ') : i(i), c(c) {}
};
You don't need to use this like that:
class A {
int i;
char c;
public:
A(int i = 0, char c = ' ') : i(i), c(c) {} // syntax makes this good
};
That is totally fine and works exactly how you want it to work.
The syntax rules mean that parameters can not appear as the variable being initialized, only its parameter. So the compiler must resolve the variables outside the parens i() as the class member and the one inside the parens (i) from the parameters because they hide the member variables due to scoping rules.
A(int i = 0): i(i) {}
^ - syntax says that i must be a member
A(int i = 0): i(i) {}
^ - syntax says normal scoping rules apply
Outside the parens it must be a class member but inside the parens the scoping rules are like in the constructor body - the parameters hide the member variables.
So it is a perfectly safe pattern to use to name your parameters the same as your data members.
It is because the this pointer needs to be inside one of the following:
cppreference:
It can appear in the following contexts:
1) Within the body of any non-static member function, including member
initializer list
2) within the declaration of a non-static member
function anywhere after the (optional) cv-qualifier sequence,
including dynamic exception specification(deprecated), noexcept
specification(C++11), and the trailing return type(since C++11)
3)
within brace-or-equal initializer of a non-static data member (since
C++11)
class Foo {
int a[sizeof(*this)]; // error, not inside a member function
};
Similarly:
class A {
int i = sizeof(*this); // valid - brace-or-equal initializer
char c;
public:
// Error, not inside member function.
A(int i = 0, char c = ' ') : this->i(i), this->c(c) // invalid
{}
};
When put inside of the body of the constructor, it will work:
A(int i = 0, char c = ' ')
{
this->c = c; // 'this->' required for disambiguation
this->i = i;
}
A(int i = 0, char c = ' ') : i(sizeof(*this)) // also valid
{}
It is often not necessary to use this-> explicitly.
I run across a weird concept named "member initializer".
Here says:
C++11 added member initializers, expressions to be applied to members
at class scope if a constructor did not initialize the member itself.
What is its definition?
Are there some examples to illustrate its usage?
It probably refers to in-class member initializers. This allows you to initialize non-static data members at the point of declaration:
struct Foo
{
explicit Foo(int i) : i(i) {} // x is initialized to 3.1416
int i = 42;
double x = 3.1416;
};
More on that in Bjarne Stroustrup's C++11 FAQ.
You can now add initializers in the class which are shared for the constructors:
class A
{
int i = 42;
int j = 1764;
public:
A() {} // i will be 42, j will be 1764
A( int i ) : i(i) {} // j will be 1764
};
It avoids having to repeat initializers in the constructor which, for large classes, can be a real win.
C++11 allows non-static member initialization like this:
class C
{
int a = 2; /* This was not possible in pre-C++11 */
int b;
public:
C(): b(5){}
};
Member initializers is referring to the extension of what initializers can be set up in the class definition. For example, you can use
struct foo
{
std::string bar = "hello";
std::string baz{"world"};
foo() {} // sets bar to "hello" and baz to "world"
foo(std::string const& b): bar(b) {} // sets bar to b and baz to "world"
};
to have bar initialized to hello if the member initializer list doesn't give another value. Note that member initializers are not restricted to build-in types. You can also use uniform initialization syntax in the member initializer list.
From here:-
Non-static Data Member Initializers are a rather straightforward new
feature. In fact the GCC Bugzilla reveals novice C++ users often tried
to use it in C++98, when the syntax was illegal! It must be said that
the same feature is also available in Java, so adding it to C++ makes
life easier for people using both languages.
struct A
{
int m;
A() : m(7) { }
};
struct B
{
int m = 7; // non-static data member initializer
};
thus the code:
A a;
B b;
std::cout << a.m << '\n';
std::cout << b.m << std::endl;