如何根据标记的参数包调用一组可变参数基类构造函数?

时间:2012-03-16 01:21:20

标签: c++ templates c++11 variadic-templates

我希望能够做到这一点:

template<typename Mix>
struct A {
  A(int i) { }
};

template<typename Mix>
struct B {
  B() { }
  B(const char*) { }
};

template<template<typename> class... Mixins>
struct Mix : Mixins<Mix<Mixins...>>... {
   // This works, but forces constructors to take tuples
   template<typename... Packs>
   Mix(Packs... packs) : Packs::Type(packs.constructorArgs)... { }
};

template<template<typename> class MixinType, typename... Args>
struct ArgPack {
  typedef MixinType Type; // pretend this is actually a template alias
  tuple<Args...> constructorArgs;
  ArgPack(Args... args) : constructorArgs(args...) { }
}

template<typename... Args>
ArgPack<A, Args...> A_(Args... args) {
  return ArgPack<A, Args...>(args...);
}

template<typename... Args>
ArgPack<B, Args...> B_(Args... args) {
  return ArgPack<B, Args...>(args...);
}

Mix<A, B> m(); // error, A has no default constructor

Mix<A, B> n(A_(1)); // A(int), B()
Mix<A, B> n(A_(1), B_("hello"); // A(int), B(const char*)

如何在这里填写/ *神秘代码* /来做我想要的,为调用一些mixins构造函数提供一个很好的界面?我有一个解决方案,通过使所有非null结构实际上采取args元组,然后重载数字输出哪个调用,但我想避免通过使他们编写构造函数A(元组)来约束mixin作者,而不只是A(int,int)。

谢谢!

2 个答案:

答案 0 :(得分:3)

我想我明白你想要什么。 std::pair有类似的功能:

std::pair<T, U> p(std::piecewise_construct
                      , std::forward_as_tuple(foo, bar)
                      , std::forward_as_tuple(qux) );
// p.first constructed in-place as if first(foo, bar) were used
// p.second constructed in place as if second(qux) were used

正如您所看到的,这有很多好处:每个都只有一个TU构造,TU都不需要例如template<int... Indices> struct indices { using next = indices<Indices..., sizeof...(Indices)>; }; template<int Size> struct build_indices { using type = typename build_indices<Size - 1>::type::next; }; template<> struct build_indices<0> { using type = indices<>; } template<typename Tuple> constexpr typename build_indices< // Normally I'd use RemoveReference+RemoveCv, not Decay std::tuple_size<typename std::decay<Tuple>::type>::value >::type make_indices() { return {}; } using tuple_type = std::tuple<int, long, double, double>;。 MoveConstructible,这只花费两个浅元组的构造。这也完美转发。但是作为一个警告,如果没有继承构造函数,这将很难实现,我将使用该功能来演示分段构造函数的可能实现,然后尝试生成它的可变版本。

但首先,当涉及到可变参数包和元组时,这个实用程序总是派上用场:

make_indices<tuple_type>()

现在,如果我们有indices<0, 1, 2, 3>,那么template<typename T, typename U> class pair { public: // Front-end template<typename Ttuple, typename Utuple> pair(std::piecewise_construct_t, Ttuple&& ttuple, Utuple&& utuple) // Doesn't do any real work, but prepares the necessary information : pair(std::piecewise_construct , std::forward<Ttuple>(ttuple), std::forward<Utuple>(utuple) , make_indices<Ttuple>(), make_indices<Utuple>() ) {} private: T first; U second; // Back-end template<typename Ttuple, typename Utuple, int... Tindices, int... Uindices> pair(std::piecewise_construct_t , Ttuple&& ttuple, Utuple&& utuple , indices<Tindices...>, indices<Uindices...>) : first(std::get<Tindices>(std::forward<Ttuple>(ttuple))...) , second(std::get<Uindices>(std::forward<Utuple>(utuple))...) {} }; 会产生template<template<typename> class... Mixins> struct Mix: Mixins<Mix<Mixins...>>... { public: // Front-end template<typename... Tuples> Mix(std::piecewise_construct_t, Tuples&&... tuples) : Mix(typename build_indices<sizeof...(Tuples)>::type {} , std::piecewise_construct , std::forward_as_tuple(std::forward<Tuples>(tuples)...) , std::make_tuple(make_indices<Tuples>()...) ) { // Note: GCC rejects sizeof...(Mixins) but that can be 'fixed' // into e.g. sizeof...(Mixins<int>) even though I have a feeling // GCC is wrong here static_assert( sizeof...(Tuples) == sizeof...(Mixins) , "Put helpful diagnostic here" ); } private: // Back-end template< typename TupleOfTuples , typename TupleOfIndices // Indices for the tuples and their respective indices , int... Indices > Mix(indices<Indices...>, std::piecewise_construct_t , TupleOfTuples&& tuple, TupleOfIndices const& indices) : Mixins<Mix<Mixins...>>(construct<Mixins<Mix<Mixins...>>>( std::get<Indices>(std::forward<TupleOfTuples>(tuple)) , std::get<Indices>(indices) ))... {} template<typename T, typename Tuple, int... Indices> static T construct(Tuple&& tuple, indices<Indices...>) { using std::get; return T(get<Indices>(std::forward<Tuple>(tuple))...); } }; 类型的值。

首先,分段构造的非变量情形:

std::tuple<indices<Indices...>...>

让我们尝试用你的mixin插入它:

int...... Indices

正如你所看到的那样,我用元组和索引元组的元组升级了一级。这样做的原因是我不能表达和匹配Mixins<...>这样的类型(相关的包被宣称为什么?Mix() = default;?),即使我做包装扩展也不是为了交易与多级包扩展太多。你现在可能已经猜到了,但是当它涉及到解决这类事情时,将它全部打包在与其索引捆绑在一起的元组中是我的运作方式...这确实有缺点但是构造是不再存在,Mix<A, B> m(std::piecewise_construct, std::forward_as_tuple(), std::forward_as_tuple());现在必须是MoveConstructible。

我建议也添加默认构造函数(即Mixin<...>),因为使用sizeof...(Mixins)看起来很傻。请注意,如果{{1}}中的任何一个不是DefaultConstructible,那么这样的默认声明将不会产生默认构造函数。

该代码已经过GCC 4.7快照的测试,并且除了{{1}}事故之外,逐字逐句。

答案 1 :(得分:0)

我在基于策略的设计环境中遇到了一个非常类似的问题。基本上我让我的类从一组策略继承行为,其中一些是有状态的,需要构造函数初始化。

我找到的解决方案是将策略类组织在一个深层次结构中,而不是一个宽层次结构。这允许编写构造函数,这些构造函数仅从参数包中获取它们所需的元素,并传递剩余的包以初始化层次结构的“顶部”部分。

这是我的代码:

/////////////////////////////////////////////
//               Generic part              //
/////////////////////////////////////////////
template<class Concrete,class Mother,class Policy>
struct PolicyHolder{};

struct DeepHierarchyFinal{};

template<class Concrete,class P,typename... Args>
struct DeepHierarchy:   public PolicyHolder<Concrete,DeepHierarchy<Concrete,Args...>,P>,
                        public P
{
    template<typename... ConstructorArgs>
    DeepHierarchy(ConstructorArgs... cargs):
    PolicyHolder<Concrete,DeepHierarchy<Concrete,Args...>,P>(cargs...){};
};

template<class Concrete,class P>
struct DeepHierarchy<Concrete,P>:   public PolicyHolder<Concrete,DeepHierarchyFinal,P>,
                                    public P
{
    template<typename... ConstructorArgs>
    DeepHierarchy(ConstructorArgs... cargs):
    PolicyHolder<Concrete,DeepHierarchyFinal,P>(cargs...){};
};
///////////////////////////////////////////
//                Test case              //
///////////////////////////////////////////

///////////////////////////////////////////
//                Policies               //
///////////////////////////////////////////
struct Policy1{};
struct Policy2{};
struct Policy3{};
struct Policy4{};

template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy1> : public Mother
{
    int x;
    template<typename... Args>
    PolicyHolder(int _x,Args... args):Mother(args...),x(_x) {};
};

template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy2> : public Mother
{
    template<typename... Args>
    PolicyHolder(Args... args):Mother(args...){
        cout<<"Policy2 initialized";
        // Here is a way to know (at runtime) if a particular
        // policy has been selected in the concrete class
        if (boost::is_convertible<Concrete,Policy3>::value)
            cout<<" together with Policy3\n";
        else
            cout<<" without Policy3\n";
    };
};

template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy3> : public Mother
{
    string s;
    char c;
    template<typename... Args>
    PolicyHolder(string _s,char _c,Args... args):Mother(args...), s(_s),c(_c) {};
};

template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy4> : public Mother
{
    template<typename... Args>
    PolicyHolder(Args... args):Mother(args...) {
        // Here is a way to check (at compile time) that 2 incompatible policies
        // does not coexist
        BOOST_STATIC_ASSERT(( ! boost::is_convertible<Concrete,Policy1>::value));
    };
};
//////////////////////////////////////////////
//              Concrete class              //
//////////////////////////////////////////////
template<class... PoliciesPack>
struct C: public DeepHierarchy<C<PoliciesPack...>,PoliciesPack...>
{
    using Policies=DeepHierarchy<C<PoliciesPack...>,PoliciesPack...>;
    string s;
    template<typename... Args>
    C(string _s,Args... args):Policies(args...),s(_s){};
};

BOOST_AUTO_TEST_CASE( testDeepHierarchyConstruction )
{
    C<Policy1,Policy2> c0("foo",4);
    BOOST_CHECK_EQUAL(c0.x,4);

    C<Policy1,Policy2,Policy3> c1("bar",3,"foo",'f');
    BOOST_CHECK_EQUAL(c1.c,'f');

    C<Policy3,Policy4> c2("rab","oof",'d');
    BOOST_CHECK_EQUAL(c2.c,'d');
}

我在this page对这种方法做了更广泛的分析。