我有兴趣实现容器容器的通用flatten
为了简化,我将使用自己的容器。主要原因是标准容器为分配器接收了一个额外的模板参数,对于我来说编写操作更复杂。因此,将容器Vector<T>
,List<T>
等视为标准容器的混淆,除了它们不需要分配器。
现在,我有一个操作flatten
操作,直到以下四个级别:
template <typename T,
template <typename> class Container1,
template <typename> class Container2>
List<T> flatten(const Container1<Container2<T>> & c)
{
List<T> ret;
for (auto & l : c)
for (auto & item : l)
ret.push_back(item);
return ret;
}
template <typename T,
template <typename> class Container1,
template <typename> class Container2,
template <typename> class Container3>
List<T>
flatten(const Container1<Container2<Container3<T>>> & c)
{
List<T> ret;
for (auto & l : c)
ret.splice(ret.end(), flatten(l));
return ret;
}
template <typename T,
template <typename> class Container1,
template <typename> class Container2,
template <typename> class Container3,
template <typename> class Container4>
List<T> flatten // and so on for more levels ...
我的问题是:
flatten
版本的复杂之处在于这些版本接收分配器作为模板参数。答案 0 :(得分:1)
这些方面的一些东西,也许是:
template <typename T>
struct LooksLikeContainer {
struct Yes{};
struct No {Yes yes[2];};
template <typename U>
static auto test(U* p) ->
typename std::enable_if<sizeof(std::begin(*p)) != 0, Yes>::type;
static No test(...);
static constexpr bool value = sizeof(test(static_cast<T*>(nullptr))) == sizeof(Yes);
};
template <typename In, typename Out>
auto flatten(const In& in, Out* out) ->
typename std::enable_if<!LooksLikeContainer<In>::value>::type;
template <typename In, typename Out>
auto flatten(const In& in, Out* out) ->
typename std::enable_if<LooksLikeContainer<In>::value>::type {
for (auto& el : in) {
flatten(el, out);
}
}
template <typename In, typename Out>
auto flatten(const In& in, Out* out) ->
typename std::enable_if<!LooksLikeContainer<In>::value>::type {
out->push_back(in);
}
答案 1 :(得分:1)
最简单的方法是递归展平容器:
template<typename Container, typename T>
void flatten_impl(Container const& c, List<T>& out)
{
for(auto const& elem : c) flatten(elem, out);
}
显然,就像任何递归一样,你也需要终止这个递归:
template<typename Elem, typename T>
void flatten_impl(Elem e, List<T>& out)
{
out.push_back(e);
}
由于这些现在含糊不清,您需要解决歧义:
template<typename Elem, typename T>
void flatten_impl(Elem e, List<T>& out, ...)
{
out.push_back(e);
}
template<typename Container, typename T>
std::void_t<typename Container::value_type> flatten_impl(Container const& c, List<T>& out)
{
for(auto const& elem : c) flatten(elem, out);
}
答案 2 :(得分:1)
使用range-v3,您可以使用ranges::view::join
,例如:
namespace detail
{
struct overload_priority_low {};
struct overload_priority_high : overload_priority_low {};
template <typename R>
R flatten(R&& r, overload_priority_low)
{
return std::forward<R>(r);
}
template <typename R>
auto flatten(R&& r, overload_priority_high)
-> decltype(flatten(std::forward<R>(r) | ranges::view::join, overload_priority_high{}))
{
return flatten(std::forward<R>(r) | ranges::view::join, overload_priority_high{});
}
}
template <typename R>
auto flatten(R&& r)
{
return detail::flatten(std::forward<R>(r), detail::overload_priority_high{});
}