我希望能够做到这一点:
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)。
谢谢!
答案 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
正如您所看到的,这有很多好处:每个都只有一个T
和U
构造,T
和U
都不需要例如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对这种方法做了更广泛的分析。