Is it possible to initialize a polymorphic C array (e.g. an array of type ParentClass*
, containing pointers of type Subclass1*
, Subclass2*
etc.) with references/pointers to temporary objects in C++?
As all temporary objects
are known at compile time, I don't want to use unique_ptr
or anything with a dynamic allocation, e.g. using new
.
Given
class Command {
public:
virtual void run(const vector<string>& args) = 0;
};
class Generate: public Command {
public:
void run(const vector<string>& args) override;
};
class Scan: public Command {
public:
void run(const vector<string>& args) override;
};
I want to define a class App
holding a static Command* commands[]
data member, e.g.:
// App.hpp
class App {
static Command* commands[];
}
// App.cpp
Generate generate;
Scan scan;
Command* App::commands[] {&generate, &scan};
While trying to find a working solution, I encountered the following list of compiler errors:
taking the address of a temporary object of type 'Generate':
Command* App::commands[] {&Generate()};`
non-const lvalue reference to type 'Generate' cannot bind to a temporary of type 'Generate'
Command* App::commands[] {&static_cast<Generate&>(Generate())};
cannot initialize an array element of type 'Command *' with an rvalue of type 'const Generate *'
Command* App::commands[] {&static_cast<const Generate&>(Generate())};
Compiler: g++: Apple clang version 15.0.0 (clang-1500.0.29.1)
.
In App.cpp
it'd love to remove the global variables generate
and scan
and use temporary objects instead, something like:
// App.cpp
Command* App::commands[] {&Generate(), &Scan()};
even better, get rid of App.cpp
and define commands
directly in App.hpp
, e.g.:
// App.hpp
class App {
static Command* commands[] {&Generate(), &Scan};
}
// App.cpp is deleted
Yes, but not with pointers. It can work only with arrays to aggregate classes containing a reference member:
struct CommandRef {
Command&& command;
};
/* DO NOT USE, SEE BELOW */
CommandRef App::commands[] {{Generate()}, {Scan()}};
However, I strongly advice against this, because the above relies on very particular lifetime extension rules for binding of references in aggregate initialization.
It can easily be made to have undefined behavior by mistake, especially if lacking understanding of the formal lifetime rules, for example by
CommandRef
a non-aggregate class.(
/)
instead of {
/}
in the aggregate-initialization.std::array
.Compilers have (at least in the past) also had bugs that caused the lifetime extension to not be correctly implemented, so that effectively the code would again have undefined behavior on them.
Instead use your original approach with named variables or just put the objects directly in the array using a std::variant
:
std::variant<Generate,Scan> App::commands[] {Generate(), Scan()};
even better, get rid of App.cpp and define commands directly in App.hpp
That's a separate issue and easy in C++17 and later: You just have to put inline
before/after static
and then it will be possible to initialize the static data member in-class the same way it would work outside of it.