【发布时间】:2016-05-17 16:45:08
【问题描述】:
template <typename Pack, typename T, std::size_t... Is> struct remove_some 从Pack 中删除T,这是找到的Is... T。例如:
template <typename...> struct P; template <typename...> struct Q;
static_assert (std::is_same<
remove_some<std::tuple<int, char, bool, int, int, double, int>, int, 1,2>,
std::tuple<int, char, bool, double, int>
>::value, "");
static_assert (std::is_same<
remove_some<std::tuple<int, char, bool, int, int, double, int>, int, 0,3>,
std::tuple<char, bool, int, int, double>
>::value, "");
static_assert (std::is_same<
remove_some<std::tuple<int, char, P<long, int, short>, bool, int, int, double, int>,
int, 0,1,2,4>,
std::tuple<char, P<long, short>, bool, int, double>
>::value, "");
这些断言都通过了我当前的代码,但问题是让这个断言通过:
static_assert (std::is_same< // Fails
remove_some<std::tuple<int, char, P<long, int, Q<int, int, int>, short>, bool, int, int, double, int>, int, 0,1,2>,
std::tuple<char, P<long, Q<int, int>, short>, bool, int, int, double, int>
>::value, "");
即一包内一包,我似乎无法确定失败的原因。这是我目前的代码,包括我认为错误的地方,但总是欢迎更好的方法。
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
template <typename Pack, typename T, std::size_t Count, typename Output, std::size_t... Is> struct remove_some_h;
template <typename Pack, typename T, std::size_t... Is>
using remove_some = typename remove_some_h<Pack, T, 0, std::tuple<>, Is...>::type;
template <template <typename...> class P, typename First, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<First, Rest...>, T, Count, std::tuple<Output...>, I, Is...> : remove_some_h<P<Rest...>, T, Count, std::tuple<Output..., First>, I, Is...> {};
// T is found, but it is not the Ith one, so do NOT remove it. Increase Count by 1 to handle the next T.
template <template <typename...> class P, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<T, Rest...>, T, Count, std::tuple<Output...>, I, Is...> : remove_some_h<P<Rest...>, T, Count + 1, std::tuple<Output..., T>, I, Is...> {};
// T is found, and it is the next one to remove, so remove it and increase Count by 1 to handle the next T.
template <template <typename...> class P, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t... Is>
struct remove_some_h<P<T, Rest...>, T, Count, std::tuple<Output...>, Count, Is...> : remove_some_h<P<Rest...>, T, Count + 1, std::tuple<Output...>, Is...> {};
// No more indices left, so no more T's to remove and hence just adjoin Rest... to the output.
template <template <typename...> class P, typename... Rest, typename T, std::size_t Count, typename... Output>
struct remove_some_h<P<Rest...>, T, Count, std::tuple<Output...>> {
using type = P<Output..., Rest...>;
static constexpr std::size_t new_count = Count;
using remaining_indices = std::index_sequence<>;
};
// No more types left to check, though there are still some T's left to remove (e.g. from an outerpack that contains this inner pack).
template <template <typename...> class P, typename T, std::size_t Count, typename... Output, std::size_t... Is>
struct remove_some_h<P<>, T, Count, std::tuple<Output...>, Is...> {
using type = P<Output...>;
static constexpr std::size_t new_count = Count;
using remaining_indices = std::index_sequence<Is...>;
};
// No more types left to check, nor any T's left to remove (this is needed to avoid ambiguity).
template <template <typename...> class P, typename T, std::size_t Count, typename... Output>
struct remove_some_h<P<>, T, Count, std::tuple<Output...>> {
using type = P<Output...>;
static constexpr std::size_t new_count = Count;
using remaining_indices = std::index_sequence<>;
};
// The problem case (dealing with inner packs):
template <typename Pack, typename T, std::size_t Count, typename Output, typename IndexSequence> struct remove_some_h_index_sequence;
template <typename Pack, typename T, std::size_t Count, typename Output, std::size_t... Is>
struct remove_some_h_index_sequence<Pack, T, Count, Output, std::index_sequence<Is...>> : remove_some_h<Pack, T, Count, Output, Is...> {};
template <template <typename...> class P, template <typename...> class Q, typename... Ts, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<Q<Ts...>, Rest...>, T, Count, std::tuple<Output...>, I, Is...> { // I is needed to avoid ambiguity.
static constexpr std::size_t new_count = Count; // I think this value is wrong?
using remaining_indices = std::index_sequence<I, Is...>; // I think this is the wrong sequence?
using inner = remove_some_h<Q<Ts...>, T, Count, std::tuple<>, I, Is...>; // Take care of the inner pack first.
using type = typename remove_some_h_index_sequence<P<Rest...>, T, inner::new_count, std::tuple<Output..., typename inner::type>, typename inner::remaining_indices>::type;
};
【问题讨论】:
-
第一个提示:失败案例实际生成的类型是
std::tuple<char, P<long, Q<int, int>, short>, bool, int, double, int>。缺少一个int。 -
“这些断言都通过了我当前的代码,但问题是让这个断言通过”——要求断言通过是原始的疯狂。
remove_some做 that 远不是人们所看到的最不意外的原则。我怀疑当你想做一些那个奇怪的事情时,你需要编写不同的原语,并组合它们来完成你的任务。 -
@Yakk。我不认为修复是那么激烈。 md51 指出了一个简单的修复方法,但是当深入一个内包时,他的修复似乎仍然失败。也许最终的解决方案与他的解决方案并没有太大的不同。另外,如果我们只是从左到右读取计数,我看不出这是多么疯狂。
标签: c++ templates c++14 variadic-templates