如何基于标记的参数包调用一组可变的基类构造函数? [英] How can I call a set of variadic base class constructors based on tagged argument packs?

查看:66
本文介绍了如何基于标记的参数包调用一组可变的基类构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够做到这一点:

  template< typename Mix> 
结构体A {
A(int i){}
};

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

template< template< typename>类... Mixins>
struct Mix:Mixins< Mix< Mixins ...>>> ... {
//可以,但是强制构造函数采用元组
template< typename ... Packs>
Mix(Packs ... packs):Packs :: Type(packs.constructorArgs)... {}
};

template< template< typename>类MixinType,类型名... Args>
struct ArgPack {
typedef MixinType Type; //假设这实际上是模板别名
tuple< Args ...> constructorArgs;
ArgPack(Args ... args):构造函数Args(args ...){}
}

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

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

Mix< A,B> m(); //错误,A没有默认的构造函数

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

我如何在这里填写/ *神秘代码* /做我想做的事情,以提供一个漂亮的接口来调用一些mixin的构造函数?我有一个解决方案,可以使所有非null构造实际上会使用一元组的args,然后重载要调用的数字,但我想避免让mixin作者编写A(元组)构造函数,而不仅仅是A(int,int)。 p>

谢谢!

解决方案

我想我知道您想要什么。 c $ c> std :: pair 具有类似的功能:

  std :: pair< T ,U> p(std :: piecewise_construct 
,std :: forward_as_tuple(foo,bar)
,std :: forward_as_tuple(qux));
// p。好像使用first(foo,bar)
//构造第二个p.second好像使用了second(qux)

如您所见,它有很多好处:正好是一个 T U 每个都进行施工, T U 都不需要MoveConstructible,这仅花费了两个浅元组的构造。这也可以实现完美的转发。但是,作为警告,如果不继承构造函数,则很难实现,并且我将使用该功能来演示分段构造函数的可能实现,然后尝试制作可变形式的构造函数。



但是首先,一个精巧的实用程序在涉及可变参数包和元组时总是会派上用场:

  template< int ...索引> 
结构索引{
using next = index< Indices ...,sizeof ...(Indices)> ;;
};

template< int Size>
struct build_indices {
using type = typename build_indices< Size-1> :: type :: next;
};
template<>
struct build_indices< 0> {
使用type = indexs> ;;
}

template< typename Tuple>
constexpr
类型名build_indices<
//通常我会使用RemoveReference + RemoveCv,而不是使用衰落
std :: tuple_size< typename std :: decay< Tuple> :: type> :: value
> :: type
make_indices()
{return {}; }

现在,如果我们有使用tuple_type = std :: tuple< int ,long,double,double> ;; ,然后 make_indices< tuple_type>()产生类型为 indices< 0的值, 1、2、3>



首先,分段构造的非可变情况:

  template< typename T,typename U> 
类对{
public:
//前端
template< typename Ttuple,typename Utuple>
pair(std :: piecewise_construct_t,Ttuple& ttuple,Utuple& utuple)
//不做任何实际工作,但是准备必要的信息
:pair(std :: piecewise_construct
,std :: forward< Ttuple>(ttuple),std :: forward< Utuple>(utuple)
,make_indices< Ttuple>(),make_indices< Utuple>())
{}

私人:
T;
U秒;

//后端
模板< typename Ttuple,typename Utuple,int ... Tindices,int ... Uindices>
对(std :: piecewise_construct_t
,Ttuple& ttuple,Utuple& utuple
,indexs< Tindices ...>,indexs< Uindices ...>) b $ b:第一(std :: get< Tindices>(std :: forward< Ttuple>(ttuple))...)
,第二(std :: get< Uindices>(std :: forward< Utuple> ;(utuple))...)
{}
};

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

  template< template< typename>类... Mixins> 
struct Mix:Mixins< Mix< Mixins ...>>> ... {
public:
//前端
template< typename ... Tuples>
Mix(std :: piecewise_construct_t,Tuples& ...元组)
:Mix(typename build_indices< sizeof ...(Tuples)> :: type {}
,std :: piecewise_construct
,std :: forward_as_tuple(std :: forward< Tuples>(元组)...)
,std :: make_tuple(make_indices< Tuples>()...))
{
// //注意:GCC拒绝sizeof ...(Mixins),但可以将其修复
//到例如sizeof ...(Mixins< int>))即使我有感觉
// GCC在这里错误
static_assert(sizeof ...(Tuples)== sizeof ...(Mixins)
,在此处放置有用的诊断信息);
}

private:
//后端
template<
类型名称TupleOfTuples
,类型名称TupleOfIndices
//元组的索引及其各自的索引
,整数...索引
>
Mix(indices< Indices ...> ;, std :: piecewise_construct_t
,TupleOfTuples&&tuple,TupleOfIndices const& index)
:Mixins< Mix< Mixins ...> >(构造< Mixins< Mix< Mixins ...>>>(
std :: get< Indices>(std :: forward< TupleOfTuples>(tuple))
,std: :get< Indices>(索引)))...
{}

template< typename T,typename Tuple,int ... Indices>
静态
T
构造(Tuple&& tuple,index< Indices ...>)
{
使用std :: get;
return T(get< Indices>(std :: forward< Tuple>(tuple))...);
}
};

如您所见,那些元组和索引元组已经上升了一个层次。这样做的原因是我无法表达和匹配 std :: tuple< indices< Indices ...> ...> 之类的类型相关的包声明为? int ......索引?),即使我这样做了,包扩展也不是为了处理过多级别的包扩展而设计的。您可能现在已经猜到了,但是在解决这类问题时,将其全部打包到一个与它的索引捆绑在一起的元组中是...的本事。... 的缺点是构造是不再存在,并且现在需要 Mixins< ...> 必须是MoveConstructible。



d也建议添加默认构造函数(即 Mix()= default; ),因为使用 Mix< A,B> m(std :: piecewise_construct,std :: forward_as_tuple(),std :: forward_as_tuple()); 看起来很傻。请注意,如果 Mixin< ...> 中的任何一个不是DefaultConstructible,则这样的默认声明将不会产生默认构造函数。



该代码已使用GCC 4.7快照进行了测试,并且可以逐字记录,但 sizeof ...(Mixins)不幸。


I'd like to be able to do this:

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*)

How do I fill in /* mysterious code here */ to do what I want, to provide a nice interface for calling some set of constructors of mixins? I have a solution that works by making all non-null constructs actually take a tuple of args, and then overloading figures out which one to call, but I would like to avoid constraining mixin authors by making them write a constructor A(tuple), instead of just A(int, int).

Thanks!

解决方案

I think I understand what you want. std::pair has a similar feature:

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

As you can see this has a lot of benefits: exactly one T and U construction each takes place, neither T and U are required to be e.g. MoveConstructible, and this only costs the constructions of two shallow tuples. This also does perfect forwarding. As a warning though, this is considerably harder to implement without inheriting constructors, and I will use that feature to demonstrate a possible implementation of a piecewise-constructor and then attempt to make a variadic version of it.

But first, a neat utility that always come in handy when variadic packs and tuples are involved:

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 {}; }

So now if we have using tuple_type = std::tuple<int, long, double, double>; then make_indices<tuple_type>() yields a value of type indices<0, 1, 2, 3>.

First, a non-variadic case of piecewise-construction:

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))...)
    {}
};

Let's try plugging that with your mixin:

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))...);
    }
};

As you can see I've gone one level higher up with those tuple of tuples and tuple of indices. The reason for that is that I can't express and match a type such as std::tuple<indices<Indices...>...> (what's the relevant pack declared as? int...... Indices?) and even if I did pack expansion isn't designed to deal with multi-level pack expansion too much. You may have guessed it by now but packing it all in a tuple bundled with its indices is my modus operandi when it comes to solving this kind of things... This does have the drawback however that construction is not in place anymore and the Mixins<...> are now required to be MoveConstructible.

I'd recommend adding a default constructor, too (i.e. Mix() = default;) because using Mix<A, B> m(std::piecewise_construct, std::forward_as_tuple(), std::forward_as_tuple()); looks silly. Note that such a defaulted declaration would yield no default constructor if any of the Mixin<...> is not DefaultConstructible.

The code has been tested with a snapshot of GCC 4.7 and works verbatim except for that sizeof...(Mixins) mishap.

这篇关于如何基于标记的参数包调用一组可变的基类构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆