4

I have some structs that all have some common functionalities that I've put in an templated base class (here Foo<int>)

template<int I>
struct Foo
{
    void foo() { std::cout << "foo: " << I << '\n'; }
};

struct Bar : Foo<1>
{
    int i;
    int j;
};

Now when I initialize any of them I have to do it like so:

Bar b{{}, 1, 2};

And I would like to be able to do it like so:

Bar b{1, 2};

And also without the boilerplate of having to write a constructor. Is there some solution to this problem?

A similar question was asked here, however, this was over 7 years ago, so I was wondering if there were any developments since then.
Also, that question is marked duplicate of a question here, however this one is slightly different, as the base struct is not empty.
In addition, I'm not really trying to accomplish anything other than adding functionality to some structures, in a more declarative way, rather than repeating the definitions of all the common methods in each of them. So any solution that achieves that, by circumventing inheritance (apart from macros ideally ;D) would work as well.

RKest
  • 93
  • 4
  • 2
    With your constraints it's not possible. You want it to stay an aggregate (no written constructor), so you must initialize with the aggregate syntax. – dalfaB May 22 '23 at 12:23

1 Answers1

5

You can use designated initializers here:

Bar b{.i = 1, .j = 2};

You can also make the aggregate struct { int i; int j; } the first thing inherited from. Here are two ways you can make that happen:

template<int I>
struct Foo
{
    void foo() { std::cout << "foo: " << I << '\n'; }
};

struct BarBase {
    int i;
    int j;
};

struct Bar : BarBase, Foo<1> {};

Bar x{{.i = 1, .j = 2}};
Bar y{1, 2};
// This way also allows you to access the base.
// A common pattern for mixins, which is what Foo is
template<typename Base, int I>
struct Foo : Base
{
    void foo() { std::cout << "foo: " << I << '\n'; }
};

struct BarBase {
    int i;
    int j;
};

using Bar = Foo<BarBase, 1>;

Bar x{{.i = 1, .j = 2}};
Bar y{1, 2};
Artyer
  • 31,034
  • 3
  • 47
  • 75
  • thats a lot of stuff to add to avoid typing {} – Serve Laurijssen May 22 '23 at 13:26
  • 1
    @​ServeLaurijssen The question makes it seem like `Foo` is the base class for many different classes. Also `Bar{{}, 1, 2};` is error prone if you use it in many places since it might accidentally initialize the first member to `0` if a class didn't happen to have an empty base class – Artyer May 22 '23 at 13:59