动态构建模板参数包

时间:2017-06-21 11:15:33

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

我试图让我的代码在类列表上调用一个方法,我希望能够在编译时配置这个列表。为了使事情更清楚(我希望),这与我目前正在做的大致相同:

  SELECT
        Item,
        Name,
        Brand,
        Date,
        StoreID
    FROM
        Item
    WHERE
        Date between '01-01-2017' and '30-01-2017' 
        OR (Brand = 'Nike' or Brand = 'Jhonson' OR Brand = 'Polo')


SELECT
    Item,
    Name,
    Brand,
    Date,
    StoreID
FROM
    Item
WHERE
    Date between '01-01-2017' and '30-01-2017' 
    AND (Brand = 'Nike' or Brand = 'Jhonson' OR Brand = 'Polo')

基本上,它调用do_something()将A,B和C中的每一个作为模板参数T。

这就是扭曲:我希望能够根据' #ifdef动态启用或禁用A,B和C.我显然可以使用像

这样的东西
template <class...> class pack {};

class A {
public:
    static void foo() {};
};
class B {
public:
    static void foo() {};
};
class C {
public:
    static void foo() {};
};

using ClassList = pack<A, B, C>;

template<class T, class ...Remaining>
void do_something(pack<T, Remaining...>) {
  // do something with T
  T::foo();
  do_something(pack<Remaining...>());
}

void do_something(pack<>) {
  // do nothing, recursion ends
}

int main() {
  do_something(ClassList());
}

但这导致2 ^ n个语句(如果n是潜在类的数量)。

我的解决方案尝试

我的解决方案尝试是为每个要添加到课程列表中的课程T添加一个&#39; AddT&#39;从pack派生的类,匹配参数包&#34; inside&#34;在模板定义中的pack类,然后添加T.以下内容:

#if defined(USE_A) && defined (USE_B) && defined (USE_C)
using ClassList = pack<A, B, C>;
#elif defined(USE_A) && defined (USE_B)
using ClassList = pack<A, B>;
…

然而,它看起来像是内心的&#39;模板参数(包)&#39;其他&#39;在定义AddC时无法访问,我得到的错误是:

using BaseClassList = pack<A, B>;

#ifdef USE_C

template <template <class ...Others> class oldpack>
class AddC : public pack<C, Others...> {};
using ClassesWithC = AddC<BaseClassList>; 

#else

using ClassesWithC = BaseClassList;

#endif

我在做一些完全愚蠢的事情吗?有没有更好的方法呢?或者上述问题可以修复吗?我很乐意使用C ++ 14(甚至是C ++ 1z,只要它由合理版本的GCC和clang实现),如果这有任何帮助。

3 个答案:

答案 0 :(得分:11)

利用C ++的空白灵活性并分别检查每个。

template<class, class...Ts>
using pack_helper = pack<Ts...>;

using ClassList = pack_helper<void
#ifdef USE_A
  ,A
#endif
#ifdef USE_B
  ,B
#endif
#ifdef USE_C
  ,C
#endif
>;

pack_helper删除其第一个类型,然后与其余类型生成pack。我们通过了void

我们这样做是因为每个其他包元素都可以用逗号开始 ,包括第一个。

但这并不像它可能有趣。

template<class...>struct pack{using type=pack; constexpr pack(){}};
template<class...Ts>constexpr pack<Ts...> pack_v{};

template<class...Ts, class...Us>
constexpr pack<Ts...,Us...>
operator+( pack<Ts...>, pack<Us...> )
{ return {}; }

constexpr auto Classes = pack_v<>
#ifdef USE_A
  +pack_v<A>
#endif
#ifdef USE_B
  +pack_v<B>
#endif
#ifdef USE_C
  +pack_v<C>
#endif
;
using ClassList = decltype(Classes);

我们将类型包添加到一起以获得我们的答案。

在C ++ 11中,将pack_v<?>替换为pack<?>{}

答案 1 :(得分:5)

如下:

using ClassList = pack<
#if defined(USE_A) 
    A,
#endif
#if defined (USE_B)
    B,
#endif
#if defined (USE_C)
    C,
#endif
    struct dummy // For comma
>;

然后提供一个帮助类来删除最后一个元素。

答案 2 :(得分:2)

只需一个接一个地添加它们

using ClassList = pack<void
#ifdef USE_A
                      ,A
#endif
#ifdef USE_B
                      ,B
#endif
#ifdef USE_C
                      ,C
#endif
                        >;

void是必需的,以便逗号使用第一个非void参数。有两种方法可以处理第一个void元素。修改do_something以对T=void不执行任何操作,或使用帮助template将其删除,如Yakk's answer中所述。