Search code examples
c++c++14list-initialization

Brace initialisation of derived types


I would like to have a type that is just a list of objects that may be derived off some base class. That is, the caller gets a list of Stuff or MoreStuff (which is derived from Stuff), and can operate on at least the Stuff parts.

This sounds like std::vector< std::unique_ptr< Stuff > > to me. However, I also want to initialise it with the brace initializer, and this is where I'm drawing a blank.

This is more or less what I'm attempting:

#include <vector>
#include <memory>
#include <string>

struct Stuff {
    std::string s1;
    std::string s2;
};

using AllStuff = std::vector< std::unique_ptr< Stuff > >;

AllStuff const MyStuff = {
    std::make_unique<Stuff>( Stuff{"1", "one"} )
};

struct MoreStuff : public Stuff {
    std::string s3;
};

AllStuff const AllMoreStuff = {
    std::make_unique<Stuff>( MoreStuff{"2", "two", "too"} )
};

Or, rather, something like that. The less typing (for adding more Stuff to the vector) the better.

The theory would be that there would be a method that would take a reference to either AllStuff or AllMoreStuff as the parameter, and be able to use s1 and s2 from it. Other methods would only take AllMoreStuff and be able to use s3 (through an appropriate use of dynamic_cast<MoreStuff*>(...)).

However, the theory and reality aren't quite lining up yet, and any help in determining how to brace-initialise a list of derived objects would be appreciated.


Solution

  • You can't use std::initializer_list with std::unique_ptr as std::unique_ptr is noncopyable and initialization with std::initializer_list implies copying.

    You could probably switch to std::shared_ptr and add constructors to your structs:

    struct Stuff {
        Stuff(const std::string& s1, const std::string& s2)
            : s1(s1)
            , s2(s2)
        {}
    
        std::string s1;
        std::string s2;
    };
    
    using AllStuff = std::vector< std::shared_ptr< Stuff > >;
    
    AllStuff const MyStuff = {
        std::make_shared<Stuff>("1", "one")
    };
    
    struct MoreStuff : Stuff {
        MoreStuff(const std::string& s1, const std::string& s2, const std::string& s3)
            : Stuff(s1, s2)
            , s3(s3)
        {}
    
        std::string s3;
    };
    
    AllStuff const AllMoreStuff = {
        std::make_shared<MoreStuff>("2", "two", "too")
    };