Search code examples
c++templatesboostboost-mplboost-fusion

How to instantiate a boost::fusion::vector member variable of a type which has no default constructor?


I am learning boost::mpl and I have the following class -

#include <string>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/at.hpp>

#include <boost/fusion/include/mpl.hpp>
#include <boost/fusion/container.hpp>


using namespace boost;
using namespace std;

template< typename T > 
class Demo
{
public:
    typedef boost::mpl::size<T> NumDimensions;

    template< size_t D >
    struct Dim
    {
        typedef typename boost::mpl::at_c< T, D >::type Type;
    };

    template< size_t D >
    typename Dim<D>::Type& GetElement()
    {
        return fusion::at_c<D>(elements_);
    }

private:
    typename fusion::result_of::as_vector< T >::type elements_;
};

This works fine, as long as I use types with default constructors (or default types)

int main(int argc, char *argv[])
{
    typedef Demo< boost::mpl::vector< int, std::string > > D1;
    D1 d;
    D1::Dim<0>::Type &i = d.GetElement<0>();

    i = "Hello World!";

    cout << " " << i << endl;
}

However, if I use a type with no default constructor, it throws a compiler error because the vector initialization fails. Is there a standard way to initialize the members properly (in a constructor) without resorting to pointers/references?


Solution

  • You can use fusion::vector's constructor:

    #include <string>
    
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/size.hpp>
    #include <boost/mpl/at.hpp>
    
    #include <boost/fusion/include/mpl.hpp>
    #include <boost/fusion/container/vector.hpp>
    #include <utility>
    
    struct foo {
        explicit foo(int){}
    };
    
    template< typename T > 
    class Demo
    {
    public:
        //You don't need to use variadic templates and perfect forwarding
        //but you may need to emulate them to get this effect (depending on
        //where exactly you need to construct Demo).
        //Another alternative would be to pass in a fusion::vector, and
        //copy construct `elements` with that vector.
        template<typename ...Args>
        Demo(Args&& ...args) : elements_(std::forward<Args>(args)...) {}
    
        typedef boost::mpl::size<T> NumDimensions;
    
        template< size_t D >
        struct Dim
        {
            typedef typename boost::mpl::at_c< T, D >::type Type;
        };
    
        template< size_t D >
        typename Dim<D>::Type& GetElement()
        {
            return boost::fusion::at_c<D>(elements_);
        }
    
    private:
        typename boost::fusion::result_of::as_vector< T >::type elements_;
    };
    
    int main() {
        Demo<boost::mpl::vector< foo, std::string > > a(foo(10),"hi");
    }