Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing member variables using the same name for constructor arguments as for the member variables allowed by the C++ standard?

Tags:

c++

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?

like image 293
Nils Avatar asked May 31 '11 08:05

Nils


2 Answers

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.

like image 81
Nawaz Avatar answered Sep 29 '22 03:09

Nawaz


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) {} }; 
like image 36
David Rodríguez - dribeas Avatar answered Sep 29 '22 03:09

David Rodríguez - dribeas