Initializing Member Variables Using the Same Name For Constructor Arguments as For the Member Variables Allowed by the C++ Standard

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

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.

difference between = and {} while initialising member variables using the same name for constructor arguments

The difference between assignment and member initialization in the special case where both identifiers are identical:

operation = operation; is an assignment from argument operation to argument operation. The argument operation eclipses the member operation.

To fix this, you had to write this->operation = operation;.

class Calculator {
Operator operation;
public:
Calculator(Operator operation) {
this->operation = operation;
}
};

This aside, the member initialization operation(operation) or operation{operation} is preferrable.

class Calculator {
Operator operation;
public:
Calculator(Operator operation):
operation{operation}
{
}
};

This works because, in the member initialization list, the scope does not (yet) contain the argument list (class scope) but the expressions in the member initializers are resolved in the scope with arguments (member function scope).


After my sloppy explanation, I recalled Constructors and member initializer lists from cppreference.com:

The body of a function definition of any constructor, before the opening brace of the compound statement, may include the member initializer list, whose syntax is the colon character :, followed by the comma-separated list of one or more member-initializers, each of which has the following syntax



class-or-identifier ( expression-list(optional) ) (1)



class-or-identifier brace-init-list (2) (since C++11)



parameter-pack ... (3) (since C++11)



1) Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization

2) Initializes the base or member named by class-or-identifier using list-initialization (which becomes value-initialization if the list is empty and aggregate-initialization when initializing an aggregate)

3) Initializes multiple bases using a pack expansion

  • class-or-identifier - any identifier, class name, or decltype expression that names a non-static data member, a direct or virtual base, or (for delegating constructors) the class itself

  • expression-list - possibly empty, comma-separated list of the parameters to pass to the constructor of the base or member

  • braced-init-list - brace-enclosed list of comma-separated initializers and nested braced-init-lists

  • parameter-pack - name of a variadic template parameter pack

I.e. for initializers

  • only the class identifier itself (for a delegating constructor call)
  • base class identifiers (for a base class constructor call)
  • and data members (for a member initializer)

are considered but not arguments (with any name).

Inside the member initializers (parentheses or curly braces), the usual name resolution of member functions applies and, hence, an argument may eclipse a member with same name.

Can I use identical names for fields and constructor parameters?

Yes it is legal and works on all platforms.
It will correctly initialize your member variable a, to the passed in value a.

It is considered by some more clean to name them differently though, but not all. I personally actually use it a lot :)

Initialization lists with the same variable name works because the syntax of an initialization item in an initialization list is as follows:

<member>(<value>)

You can verify what I wrote above by creating a simple program that does this: (It will not compile)

class  A
{

A(int a)
: a(5)//<--- try to initialize a non member variable to 5
{
}
};

You will get a compiling error something like: A does not have a field named 'a'.


On a side note:

One reason why you may not want to use the same member name as parameter name is that you would be more prone to the following:

class  A
{

A(int myVarriable)
: myVariable(myVariable)//<--- Bug, there was a typo in the parameter name, myVariable will never be initialized properly
{
}
int myVariable;
};

On a side note(2):

One reason why you may want to use the same member name as parameter name is that you would be less prone to the following:

class  A
{

A(int myVariable_)
{
//<-- do something with _myVariable, oops _myVariable wasn't initialized yet
...
_myVariable = myVariable_;
}
int _myVariable;
};

This could also happen with large initialization lists and you use _myVariable before initializing it in the initialization list.

Infinite loop when copying a class containing a BGL graph

Resolved, I had undefined behavior because I was storing vertex_descriptor and trying to use it in another graph causing this effect, these vertex_descriptors were invalid.

Usage of parameter and member variable in constructor

I personally prefer to use the constructor parameter in order to avoid using a not initialized yet member variable.

Indeed, in this example:

class Foo {
private:
int mEntity;
int mSpeed;
public:
Foo(int speed) :
mSpeed(speed),
mEntity(mSpeed)
{ }
}

the initialization of mEntity will occur before the initialization of mSpeed (because it is declared before). Therefore you will initialize mEntity with a non initialized mSpeed.

--

And inside the constructor body itself, I would also use the constructor parameter because it is a bit more straightforward while debugging to see that you use speed to initialize mMonster and not mSpeed which is itself initialized with speed. Sure it is a minimalistic overhead, but as we can avoid it easily, I think it is better to do it this way.



Related Topics



Leave a reply



Submit