2

My parser is creating an abstract syntax tree, composed of NodeSomething structs which inherit from an empty Node base struct. These are stored in a std::list<Node>.

My issue is that I don't want to write constructors for every single NodeSomething, and since the structs all inherit from a base class they are no longer aggregates, and as such I can't use initaliser lists (brace initalisation).

Since all of these structs are fairly simple, mostly containing one or two int or string variables, it seems very convoluted to have to write basic constructors for all of them. I'd remove the need for inheritance, but can't see any better way to create a generic list of a variety of these Node subclasses.

Anyway here's some trimmed code samples to explain what I mean:

struct Node {};

struct NodePerson : Node
{
    std::string name;
    int age;
};

struct NodeVariable : Node
{
    int val;
};

And then in the implementation something like this:

std::list<Node> tree;

tree.push_back(NodePerson {"Paul", 23});

This will spit out an no matching constructor for initalization of 'NodePerson' error, and then 3 notes about argument mismatch (2 given 1 expected) for the implicit move, copy, and default constructors.

From what I understand this is expected behaviour since the structs aren't aggregates anymore and can't use brace initalisation, but having to write constructors for each of them seems very unweildly. Is there a simple solution to this?

Edit: Since I didn't make it clear Node is intended to be an abstract class used only for inheritance, so that I can store a list of different Node types.

tokamach
  • 21
  • 2
  • Remove the `NodePerson` class? You're storing `Node` objects anyway, so you don't need it. – juanchopanza Oct 26 '17 at 20:57
  • If your class hierarchy really looks like this, then a pointer to `Node` is basically useless: it has no methods, so you can't do anything with it. You might want to rethink your design. – Brian Bi Oct 26 '17 at 21:01
  • I never store any actual instances of `Node`, each `NodeSomething` struct corresponds to a keyword/instruction which will be run by a sort of virtual machine, so the struct type itself carries useful information, and its variables are basically arguments to the instruction. – tokamach Oct 26 '17 at 21:03
  • 2
    The code you posted says you do: `std::list tree;`. – juanchopanza Oct 26 '17 at 21:04
  • Aannnndd I just realised that even though speed isn't an issue in this progam using reflection to decide what action to execute is probably a very poor design pattern. I think you're right in that I should rethink the design. – tokamach Oct 26 '17 at 21:05
  • @juanchopanza I only intend to use classes inheriting from `Node` in the list, I should have mentioned that `Node` is supposed to be an abstract base struct. – tokamach Oct 26 '17 at 21:06
  • 2
    If it is abstract, then you can't store it in a list because you can't instantiate it. – juanchopanza Oct 26 '17 at 21:11
  • @tokamach But the list you've shown stores `Node` objects by value, so there's no polymorphism possible. – Angew is no longer proud of SO Oct 26 '17 at 21:11
  • 1
    @Angew Oh man this is why I'm a C++ beginner. I never knew about slicing and needing to use pointers or references for polymorphic Lists, although reading about it it makes perfect sense. Thanks for clearing up my stupidity, I think I'll close the question since I've clearly got more than one problem here and go do a lot more reading on polymorphism in C++. – tokamach Oct 26 '17 at 21:19
  • For good reading, you might want to check out our [list of good C++ books](https://stackoverflow.com/q/388242/1782465). – Angew is no longer proud of SO Oct 26 '17 at 21:24
  • It'd be nice if you could just write something like `declare_aggregating_constructor;` which provides a constructor that does the same thing as aggregate initialization would do – M.M Oct 26 '17 at 23:49

1 Answers1

0

You are attempting to use aggregate initialization, which is the process of providing a braced list of initializers for each data member of an object, bypassing any constructor the object might have.

However, the presence of a base class disqualifies the object from being an aggregate (in C++14 and earlier); and therefore it cannot have aggregate initialization applied.

In C++17, the definition of "aggregate" is being expanded and your code will become legal. If you can't use a C++17 compiler yet, then you will have to fall back to other options such as adding a constructor.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    Unfortunately, the code is still not legal. An empty `{}` is still needed. See [this post](https://stackoverflow.com/q/55592478/5376789) for more details. – xskxzr Apr 10 '19 at 08:12