问题
In C++, you can declare an array of something at the file scope:
static foo a[] = { foo(), foo(), foo() };
The individual foo objects have static storage (i.e. they are not allocated at run-time).
If I had a base class inherited by two or more derived classes, the following would compile but not work as expected due to slicing:
static base a[] = { derived1(), derived2() };
Something like this should not cause slicing to happen:
static derived1 d1;
static derived2 d2;
static base *a[] = { &d1, &d2 };
My question is: How can I do the same without having to declare d1
and d2
separately from a
, and while keeping static storage for the individual (pointed-to) elements? The following gives a "taking address of temporary" error:
static base *a[] = { &derived1(), &derived2() };
Maybe it would be possible to define a constexpr
variadic template function? Something like:
template<typename... Args>
constexpr std::initializer_list<base *> base_array(Args... args) {
...
}
Then I could write:
static base *a[] = base_ptr_array(derived1(), derived2());
Maybe this would have the same "taking address of temporary" problem, though my idea was that since this is a constexpr it would work similarly to { foo(), foo(), foo() }
above (which doesn't create temporaries).
回答1:
You can use some template to avoid declaring those static variables:
#include <tuple>
#include <array>
#include <type_traits>
#include <utility>
template<class Base, class... Ts>
struct foo {
foo()
: foo(Ts{}..., std::index_sequence_for<Ts...>{})
{}
std::tuple<Ts...> deriveds;
std::array<Base*, sizeof...(Ts)> arr;
private:
template<std::size_t... Is>
foo(Ts... ts, std::index_sequence<Is...>)
: deriveds(ts...)
, arr{ &std::get<Is>(deriveds)...}
{}
};
// test
#include <iostream>
struct A {
virtual void show() const {
std::cout << "A\n";
}
virtual ~A() = default;
};
struct B: public A
{
void show() const override {
std::cout << "B\n";
}
};
struct C: public A
{
void show() const override {
std::cout << "C\n";
}
};
foo<A, A, B, C> f;
int main() {
for ( A *ptr : f.arr ) {
ptr->show();
}
}
回答2:
To be honest, I think you are trying to use the wrong tool for the job. Here's how I see it:
- You want polymorphic behavior from the array elements.
- You have a finite set of classes you care about. If it was open, you wouldn't be able to write an initializer for an array.
This reads "closed set" polymorphism to me. And you can accomplish it without dynamic allocation in c++17. You don't even need the classes to have a common base class. Only to have the same members you wish to call. All it takes is a std::variant of types, and an array of variants:
using variant_type = std::variant<derived1, derived2, ..., derived_n>;
static variant_type a[] = {
derived1(), derived2(), ..., derived_n()
};
And there you have it. You get your polymorphic behavior, just need to std::visit an array element instead of calling a member function via pointer:
for(auto& v : a)
std::visit([](auto& e) {
// Do something with e
// It's a reference to one of the types the variant can hold
}, v);
来源:https://stackoverflow.com/questions/48851186/in-c-how-can-i-create-a-stdinitializer-listbase-without-new-and-witho