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?

1 Answer 1

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];
};
Sign up to request clarification or add additional context in comments.

3 Comments

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 issue
@Amxx 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.
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.