简体   繁体   中英

Combination of variadic templates and multiple inheritance

For the sake of learning one or two things about variadic templates I stumbled across this (hopefully not that practical) problem. I have the following example code:

#include <utility>
#include <tuple>
#include <iostream>

template <typename... Args>
auto args(Args&&... args) {
    return std::make_tuple(std::forward<Args>(args)...);
}

template <class C, class TupleArgs, std::size_t... Indices>
C construct_class_unpacked_(TupleArgs&& args, std::index_sequence<Indices...>) {
    return C(std::get<Indices>(std::forward<TupleArgs>(args))...);
}

template <class C, typename TupleArgs>
C construct(TupleArgs&& args) {
    return construct_class_unpacked_<C>(std::forward<TupleArgs>(args), std::make_index_sequence<std::tuple_size<typename std::decay<TupleArgs>::type>::value>());
}

struct Root {
    Root(int l) : l_(l) {
    }

    protected:
        Root() : l_(-2) {}

    public:
        int l_;
};

struct A : public virtual Root {
    A(int i, float f) : i_(i), f_(f) {}

    int i_;
    float f_;
};

struct B : public virtual Root {
    B(int j, float g) : j_(j), g_(g) {}

    int j_;
    float g_;
};

struct C : public virtual Root {
    C() : x_(1), y_(3.1) {
    }

    int x_;
    float y_;
};

template <typename Base, typename... Bases>
struct S : public Bases... {
    template <typename Arg, typename... TupleArgs>
    S(Arg&& arg, TupleArgs&&... args) : Base(std::forward<Arg>(arg)), Bases(construct<Bases>(std::forward<TupleArgs>(args)))... {
    }
};

int main (int argc, char const* argv[]) {
    {
        S<Root, A, C, B> s(4, args(2, 3.1f), args(), args(3, 5.3f));
        std::cout << s.i_ << "\n";
        std::cout << s.f_ << "\n";
        std::cout << s.j_ << "\n";
        std::cout << s.g_ << "\n";
        std::cout << s.x_ << "\n";
        std::cout << s.y_ << "\n";
        std::cout << s.l_ << "\n";
    }
}

The original problem here was: How would I pass constructor parameters to multiple base classes. Just using variadic template arguments would restrict me to one argument per base class. If however I wanted to have a variable number of arguments per base class the most clever thing I could come up with was to use the "old" tuple-as-a-way-around cheat.

Obviously this "solution" has the caveat, that the A,B,C intermediate classes are constructed and moved/copied, therefore the Root constructor gets called each time separately. Using real base class constructors with a fixed count of base classes would result in Root::Root being called exactly once (well, there's still the diamond problem...).

My question is: Do you have any ideas how to solve this problem more elegantly (apart from the obvious solution to just avoid variadic multiple inheritance).

And keep in mind that this question is more of an academic nature, so "why not tell us what real problem you want to solve?" questions are quite pointless.

You can use a helper class to perform the initialization of the individual bases. (It's not a wrapper as I've originally called it; it doesn't use aggregation but inheritance.)

template<class Tuple>
auto make_index_sequence_for_tuple_expansion(Tuple&&)
{
    using decayed_tuple = typename std::decay<Tuple>::type;
    return std::make_index_sequence<std::tuple_size<decayed_tuple>::value>();
}

template<class T>
struct unpacker : T
{
    template<class Tuple, std::size_t... Is>
    unpacker(Tuple&& tuple, std::index_sequence<Is...>)
        : T( std::get<Is>(std::forward<Tuple>(tuple))... )
    {}

    template<class Tuple>
    unpacker(Tuple&& tuple)
        : unpacker( std::forward<Tuple>(tuple),
                    make_index_sequence_for_tuple_expansion(tuple) )
    {}
};

template <typename Base, typename... Bases>
struct S : public unpacker<Bases>... {
    template <typename Arg, typename... TupleArgs>
    S(Arg&& arg, TupleArgs&&... args)
        : Base(std::forward<Arg>(arg))
        , unpacker<Bases>(std::forward<TupleArgs>(args))...
    {}
};

Live example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM