Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C++, how can I create a `std::initializer_list<base *>` without new and without declaring individual elements separately?

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).

like image 864
Vegard Avatar asked Dec 10 '22 07:12

Vegard


1 Answers

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();
    }
}
like image 151
llllllllll Avatar answered Dec 29 '22 02:12

llllllllll