动态构建模板参数包

Luk*_*rth 2 c++ templates variadic-templates c++11 c++14

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

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());
}
Run Code Online (Sandbox Code Playgroud)

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

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

#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>;
…
Run Code Online (Sandbox Code Playgroud)

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

我的解决方案尝试

我的解决方案尝试是为每个要添加到类列表中的类T添加一个派生自派生的'AddT'类,在其模板定义中匹配包类的"内部"参数包,然后添加T.有点像:

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
Run Code Online (Sandbox Code Playgroud)

但是,在定义AddC时看起来"内部"模板参数(pack)'Others'是不可访问的,我得到的错误是:

error: 'Others' was not declared in this scope
Run Code Online (Sandbox Code Playgroud)

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

Yak*_*ont 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
>;
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

我们在其中添加类型包以获得我们的答案.

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

  • 实际上,比第一种类型更容易丢弃第一种类型. (6认同)

Jar*_*d42 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
>;
Run Code Online (Sandbox Code Playgroud)

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