基本思路和@Brian的回答一样:
- 元函数必须跟踪已使用的值(或等效的下一个可用值)和结果类型。
- 由于列表中每种类型的处理都依赖于前一种类型,简单的包扩展是不可行的,需要使用递归来处理。
细微的差别是我没有使用单独的计数器类型,并且我执行的是前序遍历而不是后序遍历。我也以不同的方式处理连接。
// This is the base case, used only when T is not a mytype.
// N is the next index available to be used.
// The third argument is used to hold types that has been processed
// during the recursion.
template<class T, int N = 0, class = mytype<-1>> struct assign_IDs {
using type = T;
static constexpr int next_index = N;
};
// When we are starting to process a mytype.
template<class T, class...Ts, int N, int M1, int M2>
struct assign_IDs<mytype<M1, T, Ts...>, N, mytype<M2>> {
// Process the first type in the list.
// The first available index is N+1 since we are using N.
using T_assigned = assign_IDs<T, N + 1>;
// recursively process the next type
using next = assign_IDs<mytype<N, Ts...>, T_assigned::next_index, mytype<N, typename T_assigned::type>>;
using type = typename next::type;
static constexpr int next_index = next::next_index;
};
// When we are in the middle of processing a mytype. The difference
// is that we won't consume an index any more.
template<class T, class...Ts, class... Vs, int N, int M1, int M2>
struct assign_IDs<mytype<M1, T, Ts...>, N, mytype<M2, Vs...>> {
// now the handling of T can start at N.
using T_assigned = assign_IDs<T, N>;
using next = assign_IDs<mytype<M1, Ts...>, T_assigned::next_index, mytype<M2, Vs..., typename T_assigned::type>>;
using type = typename next::type;
static constexpr int next_index = next::next_index;
};
// end of recursion: all types have been processed.
// The resulting type is just the third template argument.
template<class... Vs, int N, int M1, int M2>
struct assign_IDs<mytype<M1>, N, mytype<M2, Vs...>> {
using type = mytype<M2, Vs...>;
static constexpr int next_index = N;
};
Demo.
A postorder traversal 实际上实现起来会更简单,因为它少了一个部分特化:
// same as before
template<class T, int N = 0, class = mytype<-1>> struct assign_IDs {
using type = T;
static constexpr int next_index = N;
};
// can merge case #2 and #3 because now the handling is the same
// as the index isn't consumed until the end of the recursion
template<class T, class...Ts, class... Vs, int N, int M1, int M2>
struct assign_IDs<mytype<M1, T, Ts...>, N, mytype<M2, Vs...>> {
using T_assigned = assign_IDs<T, N>;
using next = assign_IDs<mytype<M1, Ts...>, T_assigned::next_index, mytype<M2, Vs..., typename T_assigned::type>>;
using type = typename next::type;
static constexpr int next_index = next::next_index;
};
// end of recursion, consume an index for the current mytype that we are processing
template<class... Vs, int N, int M1, int M2>
struct assign_IDs<mytype<M1>, N, mytype<M2, Vs...>> {
using type = mytype<N, Vs...>;
static constexpr int next_index = N + 1;
};
OTOH,我喜欢在显示类型时将数字按升序排列。