Enable default initializer list constructor
Asked Answered
C

1

13

I believe modern C++ initializer lists are very useful for initializing objects, to the point of removing the need for defining your own constructor:

struct point
{
    float coord[3];
};

point p = {1.f, 2.f, 3.f}; // nice !

However, this doesn't work when my class inherits from another class:

template<typename T>
class serializable
{
    protected:
        serializable() = default;
    ...
    // other stuff
}

struct point : public serializable<point>
{
    float coord[3];
};
point p = {1.f, 2.f, 3.f}; // Doesn't work :(

I tried adding point() = default; to my point class, but that didn't work either. How can I still initialize point with an initializer list?

Catricecatrina answered 30/4, 2015 at 14:18 Comment(0)
Z
12

Your original case relied upon aggregate initialization [dcl.init.list]:

List-initialization of an object or reference of type T is defined as follows:
...
— Otherwise, if T is an aggregate, aggregate initialization is performed

Where an aggregate and aggregate initialiazation are, from [dcl.init.aggr], emphasis mine:

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause.

But now, since point has a base class (serializable<point>), point is no longer an aggregate and no longer supports aggregate initialization.

The solution is to simply provide such a constructor to initialize point:

struct point : public serializable<point>
{
    template <typename... T>
    point(T... ts) 
    : coord{ts...}
    { } 

    float coord[3];
};
Zetland answered 30/4, 2015 at 14:24 Comment(3)
Thank for you clear and detailed answer. That juste makes me wonder why the standart is as such. I'm guessing it's because constructor s wouldn't otherwise be called ... and maybe in some cases they might be conflits between the constructors and the list initialization. However as loung as all constructors are = default that should not cause any issueCatricecatrina
@Catricecatrina What if the base classes had members? What if those members were default-constructible? It's unclear what the "right thing" to do in all of these cases - so the standard chooses to not guess.Zetland
You can also stuff your data into a member or base class, like struct point_data { float coord[3]; }, then have a "perfect forwarding constructor" that does {} based construction of it.Fiducial

© 2022 - 2024 — McMap. All rights reserved.