dimanche 30 décembre 2018

What are the rules for using constructors vs braced initializer lists for initializing classes and structs?

I've searched the answer(s) to this question online but I have yet to find a satisfactory answer. I was wondering what are all the rules for initialization of objects of struct and class types, specifically when it comes to constructors vs braced initializer lists. Are the rules different for structs vs classes too?

Let us suppose we have a class or struct called Rectangle.

#include <iostream>
using namespace std;

class Rectangle {
  public:
    Rectangle() : x(5.0), y(6.0), width(7.0), height(8.0) {}

    void printMe()
    {
        cout << "The rectangle is located at (" << x << ',' << y << ") and is " << width << " x " << height << endl;
    }
    double x;
    double y;
    double width;
    double height;
};


int main()
{
    Rectangle r = {0.0, 0.0, 3.0, 4.0};
    r.printMe();

    Rectangle s;  // uninitialized!
    s.printMe();
}

I attempt to initialize Rectangle r the way you would usually do it in C, using a plain old braced initializer list. However, g++ gives the following error:

constructor_vs_initializer_list.cpp: In function ‘int main()’:
constructor_vs_initializer_list.cpp:21:38: error: could not convert ‘{0.0, 0.0, 3.0e+0, 4.0e+0}’ from ‘<brace-enclosed initializer list>’ to ‘Rectangle’
     Rectangle r = {0.0, 0.0, 3.0, 4.0};
                                      ^

Hmmm.... That is not very useful error message it seems at first glance. However I think that it has something to do with the constructor, because if I remove it, the code compiles and runs! It would be a paradox I think, both the braced initializer list and the constructor are competing to initialize the data members it seems.

However, when I made the data members private, after removing the constructor that is, the same error message was displayed again!

I wonder what are the rules of precedence in initialization of data members. How does the braced initializer list compare to a constructor you defined yourself? How does it compare to the C++11 features: = default constructor and in-class member initializers? I assume that these different ways of initializing the object's data members would conflict with each other somehow.

Rectangle() = default;
...
double x = 1.0;

I'm not writing that mixing them up is necessarily good code, just it is code, and code that should be understood well in my opinion. Thank you.

Aucun commentaire:

Enregistrer un commentaire