Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass a list of derived types in order to store them as a member

I have a class widget.
I have an abstract class base with derivates derived_a, derived_b, etc.

I want widget to hold an arbitrary amount of objects that are derivated from base in order to later use them polymorphically.

My first attempt looks like this:

#include <vector>
#include <ostream>
#include <iostream>
#include <memory>

class widget {
public:
    explicit widget(std::vector<std::unique_ptr<base>>&& params) :
    members {std::move (params)}
    {            
    }

private:
    std::vector<std::unique_ptr<base>> members;
};

And would be called like this:

std::vector<std::unique_ptr<base>> v;
v.push_back(std::move(std::make_unique<derived_a>()));
widget w (std::move(v));

However, this solution seams unnessesarry verbose and not user friendly at all, especially when providing multiple types:

std::vector<std::unique_ptr<base>> v;
v.push_back(std::move(std::make_unique<derived_a>()));
v.push_back(std::move(std::make_unique<derived_b>()));
v.push_back(std::move(std::make_unique<derived_c>()));
v.push_back(std::move(std::make_unique<derived_a>()));
v.push_back(std::move(std::make_unique<derived_b>()));
v.push_back(std::move(std::make_unique<derived_c>()));
widget w {std::move(v)};

Instead, I would prefer usage along the lines of

widget w {derived_a(), 
          derived_b(), 
          derived_c(), 
          derived_a(), 
          derived_b(), 
          derived_c()};

so that widget is provided with a list of rvalues that it then can turn into std::vector<unique_ptr<base>>.
I have the impression that this can be achieved through templating the ctor, but, despite intensive googling, I have no clue on how to achieve my goal exactly.

Please note that the class template solution that would look tike this:

widget<derived_a, 
       derived_b, 
       derived_c, 
       derived_a, 
       derived_b, 
       derived_c> w;

Is undesirable, as I need to provide some deriveds with parameters.

like image 613
Jan Hohenheim Avatar asked Oct 28 '16 08:10

Jan Hohenheim


1 Answers

Unfortunately, you cannot use initializer_list, but you can use variadic template:

class widget {
public:
    template <typename ... Ts>
    explicit widget(Ts&&... params) 
    {
        int dummy[] =
            {0, (members.emplace_back(std::make_unique<Ts>(std::forward<Ts>(params))), 0)...};
        static_cast<void>(dummy); // avoid unused variable warning
    }

private:
    std::vector<std::unique_ptr<base>> members;
};

Demo

or in C++17, with folding expression:

    template <typename ... Ts>
    explicit widget(Ts&&... params) 
    {
        (members.emplace_back(std::make_unique<Ts>(std::forward<Ts>(params))), ...);
    }
like image 106
Jarod42 Avatar answered Oct 14 '22 07:10

Jarod42