DisplayName
DisplayName

Reputation: 249

How to use aggregated initialization in derived class with CRTP pattern?

The standard that I use is c++17.

The problem is that I want to remake my code and reorganize the structure of my code with CRPT because it fits very well but the problem is that previous code uses aggregated initialization within classes like:

ClassWithNoParent get(const T_& unit) const {
        return {.w = 1,
                .h = 1,
                .c = 1,
                .n = 1};
    }

and it's fine. When I use CRTP

class DefaultClass {
 public:
    int n;
    int c;
    int h;
    int w;
};

template <class SuccessorT, class T = DefaultClass>
class BaseCRTPClass {
 public:
    int w;
    int h;
    int c;
    int n;

    SuccessorT get(const DefaultClass&) const {
        return   {.w = 1,
                  .h = 1,
                  .c = 1,
                  .n = 1};
    }
};

class Successor : public BaseCRTPClass<Successor> {};

int main(){
    Successor t;
    auto k = t.get(DefaultClass{});
}

The compilation fails with the error

21:25: error: could not convert '{1, 1, 1, 1}' from '<brace-enclosed initializer list>' to 'Successor'

It's expected because standard wants Successor to be aggregated, however I am not quite sure that c++17 strictly forbids to have no base class. It restricts constructor (as I understood, but I may be wrong). So, how could I get this problem bypass?

How can I keep aggregated initialization for the derived class that is defined by CRTP?

P.S. Why do I want to keep the aggregated initialization? Because many places in my code uses this initialization, but if will remake in CRTP, then everything will be crushed and I will be obliged to replace all aggregated initializations on some constructors...

Upvotes: 1

Views: 223

Answers (1)

parktomatomi
parktomatomi

Reputation: 4079

The problem is unrelated to CRTP. Aggregate initialization of derived classes was only introduced in C++17. To aggregate-initialize a derived class, the base class must be initialized as the first item, in its own initializer list. So to get this working, use a double-brace:

return {{.w = 1, .h = 1, .c = 1, .n = 1}};

To illustrate, say you have a simpler, non-template class:

struct Base { int a; };
struct Derived : public Base { int b; };

You have to initialize Base as the first item:

Derived x = { 
    { .a = 42 }, // Base
    .b = 24      // b
};

Upvotes: 2

Related Questions