这里有两个问题。首先是让它编译。二是能够拨打doSomething。
第一个问题的简单解决方案是:
template <class T>
class MyClassFunction {
public:
virtual void doSomething(T object) = 0;
};
template <typename... TypesT>
class MyClass : public MyClassFunction<TypesT>...
{
uint8_t field[maxSizeOf<TypesT...>()];
};
这有一个缺点,就是很难调用doSomething:
prog.cpp:33:59: error: request for member ‘doSomething’ is ambiguous
using foo = decltype( ((MyClass<int, char>*)nullptr)->doSomething(7) );
^~~~~~~~~~~
我们需要一些usings 来解决这个问题。在c++17 我们可以这样做:
template <typename... TypesT>
class MyClass : public MyClassFunction<TypesT>...
{
uint8_t field[maxSizeOf<TypesT...>()];
public:
using MyClassFunction<TypesT>::doSomething...;
};
但这在 c++11 中不可用。
要解决这个问题,我们必须进行基于树的继承。最简单的基于树的是线性的:
template<class...Ts>
struct MyClassFunctions {};
template<class T0, class T1, class...Ts>
struct MyClassFunctions<T0, T1, Ts...>:
MyClassFunction<T0>, MyClassFunctions<T1, Ts...>
{
using MyClassFunction<T0>::doSomething;
using MyClassFunctions<T1, Ts...>::doSomething;
};
template<class T0>
struct MyClassFunctions:MyClassFunction<T0> {};
template <typename... TypesT>
class MyClass : public MyClassFunctions<TypesT...>
{
uint8_t field[maxSizeOf<TypesT...>()];
};
Live example.
这具有创建 O(n^2) 总类型名称长度的缺点,这可能会导致长类型列表出现问题。中等长度会导致内存膨胀和编译时间变慢,而长时间会导致编译器崩溃。
要解决这个问题,您可以构建一个二叉继承树。诀窍是能够使用对数深度模板递归将... 包分成两半。一旦你有了它,代码就变成了:
template<class T0, class T1, class...Ts>
struct MyClassFunctions<T0, T1, Ts...>:
left_half< MyClassFunctions, T0, T1, Ts... >,
right_half< MyClassFunctions, T0, T1, Ts... >
{
using left_half< MyClassFunctions, T0, T1, Ts... >::doSomething;
using right_half< MyClassFunctions, T0, T1, Ts... >::doSomething;
};
但是,只有在传入的类型超过几十种时,这种努力才值得。
左/右半部分如下所示:
template<template<class...>class Z, class...Ts>
using left_half = /* todo */;
template<template<class...>class Z, class...Ts>
using right_half = /* todo */;
在 todo 中有一些疯狂的元编程。
您可以使用索引技巧和std::tuple 的机制来拆分这些列表(在c++11 log-depth index generation 中需要一些努力)。或者您可以对类型列表进行指数拆分。
写
template<class...Ts>
struct pack {};
template<std::size_t N, class Pack>
struct split/* {
using lhs = // part before N
using rhs = // part after N
};
拆分类型列表,第一个 N 在左侧。可以递归写:
template<std::size_t N, class...Ts>
struct split<N, pack<Ts...>> {
private:
using half_split = split<N/2, pack<Ts...>>;
using second_half_split = split<N-N/2, typename half_split::rhs>;
public:
using lhs = concat< typename half_split::lhs, typename second_half_split::lhs >;
using rhs = typename second_half_split::rhs;
};
template<class...Ts>
struct split<0, pack<Ts...>> {
using lhs=pack<>;
using rhs=pack<Ts...>;
};
template<class T0, class...Ts>
struct split<1, pack<T0, Ts...>> {
using lhs=pack<T0>;
using rhs=pack<Ts...>;
};
这需要concat<pack, pack> 做显而易见的事情。
现在你需要apply<template, pack>,然后写left_half和right_half。