Can C++ aggregate initialization be used to construct an instance of a class which implements an interface?

  • A+
Category:Languages

I am hoping someone can give me the technical details of why the following will not compile, and if possible, a work around.

I have an existing struct called Foo, and code which uses initializer lists to create instances of Foo. This code compiles and works:

struct Foo {     int id1;     int id2; };  int main() {     Foo f({1,2});      return f.id1; } 

I would like Foo to implement an interface going forward:

struct Interface {     // All pure virtual methods, but this won't compile even if empty };  struct Foo : public Interface{     int id1;     int id2; };  int main() {     Foo f({1,2});      return f.id1; } 

This code no longer compiles, with errors in the vein of

cannot convert argument 1 from 'initializer list' to 'const _Ty &' 

(Error changes depending on your exact compiler.)

I have found this section of the standard relating to aggregate initialization:

[dcl.init.aggr]/1 An aggregate is an array or a class (Clause 12) with 1.1 no user-provided, explicit, or inherited constructors (15.1), 1.2 no private or protected non-static data members (Clause 14), 1.3 no virtual functions (13.3), and 1.4 no virtual, private, or protected base classes (13.1).

Though I am not actually sure if aggregate initialization is what's occurring here. Can someone explain the error that's occurring, and if possible, offer changes I could make to the interface? I have several existing structs which need this interface, and lots of existing code which uses this form of initialization, and I'd like to rewrite as little of it as possible. Thank you!

 


You need to initialize the base class even though it is empty:

 Foo f({{},1,2}); 

see it live

Further down in the standard in the section you are referring to we can see an example of this in [dcl.init.aggr]p4.2:

struct base1 { int b1, b2 = 42; }; struct base2 {   base2() {    b3 = 42;  }  int b3; };  struct derived : base1, base2 {  int d; };  derived d1{{1, 2}, {}, 4}; derived d2{{}, {}, 4}; 

initializes d1.b1 with 1, d1.b2 with 2, d1.b3 with 42, d1.d with 4, and d2.b1 with 0, d2.b2 with 42, d2.b3 with 42, d2.d with 4. —end example]

Also see [dcl.init.aggr]p2 which explains what the elements of an aggregate are:

The elements of an aggregate are:

-for an array, the array elements in increasing subscript order, or
-for a class, the direct base classes in declaration order, followed by the direct non-static data members ([class.mem]) that are not members of an anonymous union, in declaration order.

Note, the answer assumes C++17 or greater since before C++17 an aggregate was not allowed to have a base class.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: