Search code examples
c++inheritancestructinitializer-list

Why does my copy list initialization stop working after I add inheritance?


I've been initializing one of my structs this way without any issue for quite some time:

struct MyStruct
{
    float firstArr[4];
    float secondArr[4];
    float thirdArr[4];
    float fourthArr[4];
};

void Function()
{
    std::vector<MyStruct> myArr;

    myArr.push_back // This works fine
    ({
        { 0, 0, 0, 0 },
        { 0, 0, 0, 0 },
        { 0, 0, 0, 0 },
        { 0, 0, 0, 0 }
    });
}

When I start deriving my struct from a Parent, I'm no longer able to do it.

struct Parent
{
    // Nothing
};

struct MyStruct: Parent
{
    float firstArr[4];
    float secondArr[4];
    float thirdArr[4];
    float fourthArr[4];
};

void Function()
{
    std::vector<MyStruct> myArr;

    myArr.push_back // This gets compiler errors
    ({
        { 0, 0, 0, 0 },
        { 0, 0, 0, 0 },
        { 0, 0, 0, 0 },
        { 0, 0, 0, 0 }
    });
}

The second code block gets compiler errors when trying to initialize that way.

Is there a constructor that I can declare that will allow me to initialize like that and still have my struct derive from another one? I don't know why it's giving me an error in the first place, especially since the parent struct is empty.

My entire project pushes back its "child" structs like this, so is there any way around this that doesn't include me changing every single initializer?

Later I'm going to need to be pushing back MyStruct objects onto a std::vector<Parent>, which could have entirely separate issues initialization wise, but I can't even seem to get past this problem first.


Solution

  • In your first example, MyStruct is an aggregate, so the members are simply copy-initialized using the values in the initializer list.

    In your second example, MyStruct is no longer an aggregate as it has a base class, so the compiler looks for a constructor instead; the only constructor available is the implicitly defined default constructor (MyStruct()), hence the error.

    One way around this would be to use std::array with a properly defined constructor

    struct MyStruct: Parent
    {
    MyStruct(
      std::array<float, 4> const& first,
      std::array<float, 4> const& second,
      std::array<float, 4> const& third,
      std::array<float, 4> const& fourth)
       : firstArr{first}, secondArr{second}, thirdArr{third}, fourthArr{fourth} {}
    
      std::array<float, 4> firstArr;
      std::array<float, 4> secondArr;
      std::array<float, 4> thirdArr;
      std::array<float, 4> fourthArr;
    };
    

    For your second question, as @FredLarson points out you're going to have a bad time pushing MyStructs to a std::vector<Parent>, you'll need to rethink your design.