多个虚拟继承和可变参数模板

时间:2017-07-21 13:52:34

标签: c++ c++11 templates

在我的项目中,我使用以下设计:

enum {
    A = 1, B = 2, C = 4
};
struct Foo { int foo; };
template <int> struct Bar;
template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };

现在我可以做一些有趣的事情:

template <> struct Bar<A|B> : public A, public B {};
template <> struct Bar<A|C> : public A, public C {};
template <> struct Bar<B|C> : public B, public C {};
template <> struct Bar<A|B|C> : public A, public B, public C {};

所以我可以写:

Bar<A|C> bar;
bar.foo = 2;
bar.a = 1;
bar.c = 2;

现在我希望在用户创建这样的实例时自动完成组合类Bar<X|Y|Z|..>的生成。 这可能是使用一些模板魔法吗?

有些事情:

template <int N, class ...Classes> struct Bar<N> : public Classes... {};
template <int N> struct Bar<N> : public Bar<N, generate_classes<N> > {};

其中generate_classes能够生成应继承的类Bar<N>列表。

任何帮助表示赞赏!

3 个答案:

答案 0 :(得分:3)

不需要花哨的schistcy SFINAE,参数包或任何这样的黑暗魔法。

enum {
    A = 1, B = 2, C = 4
};

struct Foo { int foo; };

template <unsigned int> struct Bar; // unsigned because bit fiddling

template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };

template <unsigned int i> struct Bar :
    public Bar<i & ~(i-1)>, // only least significant set bit 
    public Bar<i &  (i-1)>  // all other set bits
{ };

// checking
int main ()
{
  Bar<A|B|C> abc;

  abc.a = 0;  // ok
  abc.b = 0;  // ok
  abc.c = 0;  // ok

  Bar<A|B> ab;

  ab.a = 0;   // ok
  ab.b = 0;   // ok
  ab.c = 0;   // error

  Bar<A|C> ac;

  ac.a = 0;   // ok
  ac.b = 0;   // error
  ac.c = 0;   // ok

  Bar<9> x;   // error
}

答案 1 :(得分:0)

不要考虑以某种方式生成要继承的类列表,而是将其视为选择它们。这个应该是遗传的,这个不应该。这可以通过模板参数的静态调度来实现。所以我们得到一个基于bool参数的模板给出T或空类。它很可能通过空基优化进行优化(在这种情况下甚至可以保证,但我不确定标准给出的确切要求),因此没有内存开销。

#include <iostream>

enum {
    A = 1, B = 2, C = 4
};

template <class T, bool Enable>
struct or_empty;

template <class T>
struct or_empty<T, false>
{
    struct empty {};
    using type = empty;
};

template <class T>
struct or_empty<T, true>
{
    using type = T;
};

template <class T, bool Enable>
using or_empty_t = typename or_empty<T, Enable>::type;

struct Foo { int foo; };

template <int I> struct Bar : 
    public or_empty_t<Bar<A>, I&A>, 
    public or_empty_t<Bar<B>, I&B>, 
    public or_empty_t<Bar<C>, I&C> {};

template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };


int main()
{
    Bar<A|C> bar;
    bar.foo = 2;
    bar.a = 1;
    // bar.b = 2; error
    bar.c = 2;

    std::cout << bar.foo << ' ' << bar.a << ' ' << bar.c << '\n';

    std::cout << sizeof(Bar<A>) << ' ' << sizeof(Bar<A|B>) << ' ' << sizeof(Bar<A|B|C>) << '\n';
}

演示:http://coliru.stacked-crooked.com/a/f170fbd873739c38

答案 2 :(得分:0)

一些编译时解压缩标志的机制:

enum flag_e { None = 0, A = 1, B = 1<<1, C = 1<<2 };

template<flag_e...>
struct flags {using type=flags; constexpr flags(){}};
template<flag_e>
struct flag{using type=flag; constexpr flag(){}};

constexpr flags<A,B,C> all_flags{};

template<flag_e...lhs, flag_e...rhs>
constexpr flags<lhs...,rhs...> operator+(flags<lhs...>, flags<rhs...>)
{ return {}; }

template<flag_e lhs, flag_e...rhs>
inline constexpr flags<lhs, rhs...> operator+(flag<lhs>, flags<rhs...>)
{ return {}; }
template<flag_e...lhs, flag_e rhs>
inline constexpr flags<lhs..., rhs> operator+(flags<lhs...>, flag<rhs>)
{ return {}; }

template<flag_e...fs>
inline constexpr flags<fs...> operator+(flag<None>, flags<fs...>)
{ return {}; }
template<flag_e...fs>
inline constexpr flags<fs...> operator+(flags<fs...>, flag<None>)
{ return {}; }

template<flag_e f, flag_e...fs>
inline constexpr auto unpack( flag<f>, flags<fs...> x, flags<> )
-> flags<fs...>
{ return {}; }

template<flag_e f, flag_e...fs, flag_e c0, flag_e...checks>
inline constexpr auto unpack( flag<f> fin, flags<fs...> x, flags<c0, checks...> )
-> decltype( unpack( fin, x+flag<flag_e(f&c0)>{}, flags<checks...>{} ) )
{ return {}; }

template<flag_e f>
inline constexpr auto unpack( flag<f> fin )
-> decltype( unpack( flag<f>{}, flags<>{}, all_flags ) )
{ return {}; }

然后我们使用它:

template <int> struct Bar;

template <class flags> struct BarImpl;
template <flag_e...fs> struct BarImpl<flags<fs...>>:
  Bar<fs>...
{};
template <int flags> struct Bar:
  BarImpl<decltype(unpack(flag<flag_e(flags)>{}))>
{};
struct Foo { int foo; };
template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };

Live example

允许您拥有标记和单个标记的代码可以更加通用,代价是更频繁地提及flag_e类型。

我过于光滑,可以说flags<A>+flags<B>并获得flags<A,B>,因为我喜欢这种符号。

然后我写了unpack,其中flag<A|B>生成flags<A,B>

在C ++ 14和17中,事物变得更加光滑,折叠并返回类型推导等。