Is the variable here 1 or 2 and is this compiler or standard dependent?
class foo {
int val = 1;
foo(int bar = 2) : val(bar) {}
}
It is 2 unless you provide a different value when calling the constructor.
See cpp reference concerning non-static data member initialisation:
If a member has a default member initializer and also appears in the
member initialization list in a constructor, the default member
initializer is ignored.
this here : foo(int bar = 2) is called default argument,
and allows you to call a function without providing that argument.
How?
if you do:
foo(100) then val is initialized with 100
but if you ommit the parameter
foo( ) then val is initialized with 2
at the end, that declaration makes this init superfluous:
int val = 1;
Related
The following code can pass compiling and will print 0 on the console. I saw similar code in STL. Does type int in C++ have a constructor? Is int() a call of some defined function?
int main()
{
int a = int();
cout << a << endl;
return 0;
}
In this context,
int a = int(); // 1)
it value-initializes a, so that it holds value 0. This syntax does not require the presence of a constructor for built-in types such as int.
Note that this form is necessary because the following is parsed as a function declaration, rather than an initialization:
int a(); // 2) function a() returns an int
In C++11 you can achieve value initialization with a more intuitive syntax:
int a{}; // 3)
Edit in this particular case, there is little benefit from using 1) or 3) over
int a = 0;
but consider
template <typename T>
void reset(T& in) { in = T(); }
then
int i = 42;
reset(i); // i = int()
first, we start with this syntax:
type variableName = type();
this syntax is called value initialization of a variableName, or in other word we say a variableName is zero initialized.
But, what is the meaning of the value initialization.
if the type of a variableName is a built-in/scalar type like (int, char, ...),
value initialization mean that a variableName is initialized with the value zero.
and if the type is a complex type like (classes, structs, ...) mean that a variableName is initialized by calling its default constructor.
int() is the constructor of class int. It will initialise your variable a to the default value of an integer, i.e. 0.
Even if you don't call the constructor explicitly, the default constructor, i.e. int() , is implicitly called to initialise the variable.
Otherwise there will be a garbage value in the variable.
When I am using vector as following in a function, I get a variable D and it works.
vector<int> D(100);
However, when I decide to use this as a class member, I get the following weird error :
error: expected identifier before numeric constant
99 | vector<int> D(100);
| ^~~
Could someone explain why this particular error ?
I can use array in a class as int D[100].
Default member initializer (since C++11) for member variable only supports equal-sign initializer (and braced-initializer, which doesn't match this use-case).
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.
You can
vector<int> D = vector<int>(100);
Or use member intializer list. e.g.
struct x {
vector<int> D;
x() : D(100) {}
};
The related code is listed below, you could check it on https://godbolt.org/z/3GH8zD.
I could solve the complier compile error indeed.But i am not completely clear about the reason lie behind it.I would be grateful to have some help with this question.
struct A
{
int x;
A(int x = 1): x(x) {} // user-defined default constructor
};
struct F : public A
{
int& ref; // reference member
const int c; // const member
// F::F() is implicitly defined as deleted
};
int main()
{
F f; // compile error
}
Compiler compliains:
Could not execute the program
Compiler returned: 1
Compiler stderr
<source>:10:15: error: declaration does not declare anything [-fpermissive]
10 | const int; // const member
| ^~~
<source>: In function 'int main()':
<source>:16:9: error: use of deleted function 'F::F()'
16 | F f; // compile error
| ^
<source>:7:12: note: 'F::F()' is implicitly deleted because the default definition would be ill-formed:
7 | struct F : public A
| ^
<source>:7:12: error: uninitialized reference member in 'struct F'
<source>:9:14: note: 'int& F::ref' should be initialized
9 | int& ref; // reference member
| ^~~
The right code may be:
struct F
{
int& ref = x; // reference member
const int c = 1; // const member
// F::F() is implicitly defined as deleted
};
The class member int& ref needs to be bound to an int when an object of type F is created.
A compiler-generated default constructor would not know how to bind it, so the compiler merely deletes the default constructor.
In the second snippet you explicitly set the member. (You can do that from C++11).
Have a look at the Deleted implicitly-declared default constructor section in cppreference. It explains under which circumstances the default constructor is deleted:
T has a member of reference type without a default initializer. (since C++11)
T has a const member without user-defined default constructor or a default member initializer (since C++11).
That is, if you don't default initialize your const and reference member variables, the constructor will be deleted and it also explains why your second attempt "worked". In fact, it doesn't work until you make F inherit from A (I guess this was your intention). Otherwise, you can't assign A::x to F::ref.
Note: Even though F::ref=A::x is allowed, there is no point in doing that since F already has access to A::x due to inheritance.
To try to build on #Bathsheba answer.
Let's first understand what a reference is. A reference is essentially a pointer that can't be null this makes them very tricky to initialize.
Let's look at your second class:
struct F : public A
{
int& ref; // reference member
const int c; // const member
// F::F() is implicitly defined as deleted
};
If instead of defining it as int& ref; we define it as int ref; the compiler will say, I have no idea what ref is supoosed to be, so I'll stick some random value into it and be done. And so the default constructor just does ref = /*some num*/;
Let's say it was a pointer instead, i.e int* ref; Once again, the default constructor needs to initialize it to a value, for pointers the most logical value for default initialization is nullptr and so we get int* ref = nullptr;
But what if it is a reference? i.e int& ref;. We can;t initialize the variable to a number, because it is not a number it's a reference "i.e a pointer" to a number. But references can't be null so we can;t use nullptr either. So there is no valid default value initialization for the reference. The only reasonable thing is then to delete the default constructor because there does not exist any default value we can use to initialize one of its members.
This question already has answers here:
Can I use identical names for fields and constructor parameters?
(6 answers)
Closed 1 year ago.
I figured out that it's possible to initialize the member variables with a constructor argument of the same name as show in the example below.
#include <cstdio>
#include <vector>
class Blah {
std::vector<int> vec;
public:
Blah(std::vector<int> vec): vec(vec)
{}
void printVec() {
for(unsigned int i=0; i<vec.size(); i++)
printf("%i ", vec.at(i));
printf("\n");
}
};
int main() {
std::vector<int> myVector(3);
myVector.at(0) = 1;
myVector.at(1) = 2;
myVector.at(2) = 3;
Blah blah(myVector);
blah.printVec();
return 0;
}
g++ 4.4 with the arguments -Wall -Wextra -pedantic gives no warning and works correctly. It also works with clang++. I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
Yes. That is perfectly legal. Fully Standard conformant.
Blah(std::vector<int> vec): vec(vec){}
^ ^
| |
| this is the argument to the constructor
this is your member data
Since you asked for the reference in the Standard, here it is, with an example.
§12.6.2/7
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.
[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
//^^^^ note this (added by Nawaz)
};
initializes X::r to refer to X::a,
initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor
parameter i, and initializes X::j with
the value of X::i; this takes place
each time an object of class X is
created. ]
[Note: because the
mem-initializer are evaluated in the
scope of the constructor, the this
pointer can be used in the
expression-list of a mem-initializer
to refer to the object being
initialized. ]
As you can see, there're other interesting thing to note in the above example, and the commentary from the Standard itself.
BTW, as side note, why don't you accept the parameter as const reference:
Blah(const std::vector<int> & vec): vec(vec) {}
^^^^const ^reference
It avoids unneccessary copy of the original vector object.
It is guaranteed always to work (I use it quite often). The compiler knows that the initializer list is of the form: member(value), and so it knows that the first vec in vec(vec) must be a member. Now on the argument to initialize the member, both members, arguments to the constructor and other symbols can be used, as in any expression that would be present inside the constructor. At this point it applies the regular lookup rules, and the argument vec hides the member vec.
Section 12.6.2 of the standard deals with initialization and it explains the process with paragraph 2 dealing with lookup for the member and paragraph 7 with the lookup of the argument.
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified. [Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
One additional counter argument or perhaps just something to be aware of is the situation in which move construction is used to intialize the member variable.
If the member variable needs to be used within the body of the constructor then the member variable needs to be explcitly referenced through the this pointer, otherwise the moved variable will be used which is in an undefined state.
template<typename B>
class A {
public:
A(B&& b): b(std::forward(b)) {
this->b.test(); // Correct
b.test(); // Undefined behavior
}
private:
B b;
};
As others have already answered: Yes, this is legal. And yes, this is guaranteed by the Standard to work.
And I find it horrible every time I see it, forcing me to pause: "vec(vec)? WTF? Ah yes, vec is a member variable..."
This is one of the reasons why many, including myself, like to use a naming convention which makes it clear that a member variable is a member variable. Conventions I have seen include adding an underscore suffix (vec_) or an m_ prefix (m_vec). Then, the initializer reads: vec_(vec) / m_vec(vec), which is a no-brainer.
This question already has answers here:
Can I use identical names for fields and constructor parameters?
(6 answers)
Closed 1 year ago.
I figured out that it's possible to initialize the member variables with a constructor argument of the same name as show in the example below.
#include <cstdio>
#include <vector>
class Blah {
std::vector<int> vec;
public:
Blah(std::vector<int> vec): vec(vec)
{}
void printVec() {
for(unsigned int i=0; i<vec.size(); i++)
printf("%i ", vec.at(i));
printf("\n");
}
};
int main() {
std::vector<int> myVector(3);
myVector.at(0) = 1;
myVector.at(1) = 2;
myVector.at(2) = 3;
Blah blah(myVector);
blah.printVec();
return 0;
}
g++ 4.4 with the arguments -Wall -Wextra -pedantic gives no warning and works correctly. It also works with clang++. I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
Yes. That is perfectly legal. Fully Standard conformant.
Blah(std::vector<int> vec): vec(vec){}
^ ^
| |
| this is the argument to the constructor
this is your member data
Since you asked for the reference in the Standard, here it is, with an example.
§12.6.2/7
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.
[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
//^^^^ note this (added by Nawaz)
};
initializes X::r to refer to X::a,
initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor
parameter i, and initializes X::j with
the value of X::i; this takes place
each time an object of class X is
created. ]
[Note: because the
mem-initializer are evaluated in the
scope of the constructor, the this
pointer can be used in the
expression-list of a mem-initializer
to refer to the object being
initialized. ]
As you can see, there're other interesting thing to note in the above example, and the commentary from the Standard itself.
BTW, as side note, why don't you accept the parameter as const reference:
Blah(const std::vector<int> & vec): vec(vec) {}
^^^^const ^reference
It avoids unneccessary copy of the original vector object.
It is guaranteed always to work (I use it quite often). The compiler knows that the initializer list is of the form: member(value), and so it knows that the first vec in vec(vec) must be a member. Now on the argument to initialize the member, both members, arguments to the constructor and other symbols can be used, as in any expression that would be present inside the constructor. At this point it applies the regular lookup rules, and the argument vec hides the member vec.
Section 12.6.2 of the standard deals with initialization and it explains the process with paragraph 2 dealing with lookup for the member and paragraph 7 with the lookup of the argument.
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified. [Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
One additional counter argument or perhaps just something to be aware of is the situation in which move construction is used to intialize the member variable.
If the member variable needs to be used within the body of the constructor then the member variable needs to be explcitly referenced through the this pointer, otherwise the moved variable will be used which is in an undefined state.
template<typename B>
class A {
public:
A(B&& b): b(std::forward(b)) {
this->b.test(); // Correct
b.test(); // Undefined behavior
}
private:
B b;
};
As others have already answered: Yes, this is legal. And yes, this is guaranteed by the Standard to work.
And I find it horrible every time I see it, forcing me to pause: "vec(vec)? WTF? Ah yes, vec is a member variable..."
This is one of the reasons why many, including myself, like to use a naming convention which makes it clear that a member variable is a member variable. Conventions I have seen include adding an underscore suffix (vec_) or an m_ prefix (m_vec). Then, the initializer reads: vec_(vec) / m_vec(vec), which is a no-brainer.