Home > Software design >  How I can keep the default contructor while also adding custom ones?
How I can keep the default contructor while also adding custom ones?

Time:07-18

If I don't define a constructor in a struct I can initialize it by just picking a certain value like this:

struct Foo {
    int x, y;
};

Foo foo = {.y = 1};

But if I add new default constructor then I loose this feature:

struct Bar {
    int x, y;
    Bar(int value) : x(value), y(value) {}
};

Bar bar1 = 1;
Bar bar2 = {.y = 2}; // error: a designator cannot be used with a non-aggregate type "Bar"

Is it possible to have both ways?

I tried adding the default constructor Bar () {} but it seems to not work either.

CodePudding user response:

You can't have your cake and eat it too. If the object has a constructor it is no longer an aggregate, and only aggregates can be initialized with designated initializers. You can't use constructors for arbitrary initialization logic with aggregates.

Are we toasted though? No, because there's the "named constructor" idiom. It's essentially just a static member function that returns an initialized object, and able to perform some logic. The idiom is compatible with aggregate initialization.

struct Foo {
    int x, y;
    static Foo filled_with(int value) {
        return {.x = value, .y = value};
    }
};

Foo foo = {.y = 1}; // Still an aggregate.
Foo foo2 = Foo::filled_with(2); // Custom logic

There's not even any copying or moving with this approach, because C 17 removed the possibility for those. foo2 is initialized directly with whatever the static member does.

CodePudding user response:

Similar to what ellipticaldoor wrote:

struct FooBase {
    int x = 0, y = 0;
};

struct Foo : FooBase {
    Foo(int x_) : FooBase{.x = x_} { }
    Foo(FooBase &&t) : FooBase{t} {}
};

Foo foo = {{.y = 1}};
Foo foo2{1};

CodePudding user response:

So far this is the closest thing I can find:

struct Vec2 {
    int x, y;
};

struct Bar {
    int x, y;
    Bar(int value) : x(value), y(value) {}
    Bar(Vec2 value) : x(value.x), y(value.y){};
};

Bar bar1 = 1;
Bar bar2 = {{.y = 2}};

But you need to use double params

CodePudding user response:

You can use a data member initializer instead so the type remains an aggregate:

struct Foo {
    int x = 0, y = x;
};

Foo foo1 = {.y = 6};  // Foo{0, 6}
Foo foo2{7};          // Foo{7, 7}

(Though it can't be implicitly constructed from int)

  •  Tags:  
  • c
  • Related