Manvendra Singh
Manvendra Singh

Reputation: 626

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

As per this answer using same name for both member variable and constructor argument is fine. So after doing some debugging, I figured why the below code was not working (the operation member of the class was never getting initialized causing the switch statements to fail.)

#include <cstdio>
#include <cstdlib>

enum class Operator {
    Add,
    Subtract,
    Multiply,
    Divide
};

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

        int calculate(int a, int b) {
            switch (operation) {
                case Operator::Add: {
                    return a+b;
                }
                case Operator::Subtract: {
                    return  abs(a-b);
                }
                case Operator::Multiply: {
                    return a*b;
                }
                case Operator::Divide: {
                    return a/b;
                }

            }
        }
};

int main() {
    Calculator calculator{Operator::Add};
    int a = 100;
    int b = 20;
    printf("%d + %d = %d\n", a, b, calculator.calculate(a, b));
}

replacing the line operation = operation with operation{operation} inside constructor fixed the issue, but I am still not getting why operation = operation was producing wrong results compared to operation{operation} and what's the difference between both in context of constructor initialization;

Upvotes: 1

Views: 110

Answers (1)

Scheff&#39;s Cat
Scheff&#39;s Cat

Reputation: 20141

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.

Upvotes: 1

Related Questions