Mikhail
Mikhail

Reputation: 11

Initialize class instance with initializer_list if class has base with virtual methods c++

How to initialize class instance with initializer_list if class has base with virtual methods c++? I want to avoid writing constructors and use "lazy" way to initialize class like X x{"Some string"};. But I got

error: no matching function for call to ‘X::X(<brace-enclosed initializer list>)’

Is any way to simply initialize X instance without significant changing of classes?

struct Y
{
    virtual ~Y() = 0;
};
struct X : public Y
{
    string msg;
    virtual ~X() = default;
};
int main()
{
    X x{"Some string"};
    return 0;
}

Upvotes: 1

Views: 253

Answers (1)

The comments already answer it. Here is a summary.

Short answer

No, there is no way - because that would be a aggregate initialisation (https://en.cppreference.com/w/cpp/language/aggregate_initialization) that is only possible when the class (struct) has:

  • no user-declared constructors
  • no user-provided, inherited, or explicit constructors
  • no user-declared or inherited constructors
  • no private or protected direct (since C++17) non-static data members
  • no base classes
  • no virtual base classes
  • no virtual member functions
  • no default member initializers

Alternative 1

If there is just the attribute string msg, then you can do the following.

#include <iostream>

using std::string;
using std::cout;

struct Y
{
    virtual ~Y() = default; // !!! not pure virtual !!!
};

struct X : public Y
{
    string msg;
    virtual ~X() = default;
};

int main( int /*argc*/, char */*argv*/[] )
{
    X   x;
    x.msg = "Some string";
    cout << "int main( int /*argc*/, char */*argv*/[] ) - #1" << '\n';
    return 0;
}

NOTE: In the code above, the destructor has been changed to virtual ~Y() = default; otherwise the compiler complains about the missing destructor that is called from ~X(). Linker error, may be:

Undefined symbols for architecture arm64:
  "m1::Y::~Y()", referenced from:
      m1::X::~X() in m1.o

NOTE: You may have no constructor that assures the consistency of states after construction.

Alternative 2

Make your attribute list a tuple.

#include <iostream>

using std::string;
using std::cout;

struct Y
{
    virtual ~Y() = default;
};

typedef string TMessage;
typedef int TOtherAttribute;

using XAttributes = std::tuple<TMessage, TOtherAttribute>;

struct X : public Y
{
    XAttributes mx;
    X( XAttributes l ) : mx{ l } {
    };
    virtual ~X() = default;
    template<typename T> void print() const {
        cout << "member = " << get<T>( mx ) << '\n';
    }
};

int main( int /*argc*/, char */*argv*/[] )
{
    X   x{ XAttributes{ "Some string", 34 } };
    x.print<TMessage>();
    x.print<TOtherAttribute>();
    return 0;
}

Output:

member = Some string
member = 34

Upvotes: 0

Related Questions