为boost::multi_array<std::list<int>, 2>实现扁平化迭代器以支持范围for循环的问题
你遇到的问题主要出在boost::multi_array的迭代器行为和普通容器(比如vector)不一样,以及原代码里的两个小bug,我来一步步帮你解决:
首先分析编译错误的原因
构造函数里的遍历问题:
对于2维的boost::multi_array<std::list<int>,2>,用range-based for循环遍历的时候,顶层循环拿到的是1维的sub_array对象,而不是直接的std::list<int>。这和vector<vector<int>>完全不同——后者的顶层迭代器直接指向vector<int>。所以你原来的代码里for (auto &d: data)拿到的d是sub_array<std::list<int>,1>,这个对象没有push_back方法,自然会报错。扁平化迭代器的end()不匹配:
原代码里NestedContainer::end()返回的是data.end(),也就是外层容器的迭代器,但begin()返回的是flattening_iterator类型,两者类型不匹配,这其实是个隐藏bug,只是普通容器碰巧没触发编译错误而已。
解决方案:分两步修复
第一步:修复构造函数的填充逻辑
我们需要针对普通容器和boost::multi_array分别处理填充逻辑,用模板特化+递归的方式遍历所有元素:
首先添加辅助填充函数,放在NestedContainer类外面:
#include <boost/type_traits/is_multi_array.hpp> // 处理普通容器(比如vector<vector<int>>、vector<list<int>>) template <typename Container> typename std::enable_if_t<!boost::is_multi_array<Container>::value, void> fill_data(Container& data, std::size_t& i) { for (auto& elem : data) { for (std::size_t j = 0; j < 3; ++j) { elem.push_back(i); } i += 3; } } // 递归处理boost::multi_array template <typename T, std::size_t N> void fill_data(boost::multi_array<T, N>& data, std::size_t& i) { if constexpr (N == 1) { // 1维multi_array直接遍历每个元素 for (auto& elem : data) { for (std::size_t j = 0; j < 3; ++j) { elem.push_back(i); } i += 3; } } else { // 多维的话,先遍历每个子数组,再递归处理 for (auto& sub_array : data) { fill_data(sub_array, i); } } }
然后修改NestedContainer的构造函数:
NestedContainer(E extent) : extent(extent), data(extent) { std::size_t i = 0; fill_data(data, i); }
第二步:适配扁平化迭代器以支持multi_array
boost::multi_array的元素是连续存储的,我们可以用data()获取第一个元素的指针,num_elements()获取总元素数,把整个multi_array转换成一个连续的std::list<int>序列,再用原来的扁平化迭代器遍历每个list的int元素。
同时修复end()的类型不匹配问题,用SFINAE区分普通容器和multi_array:
template <typename Container = T> typename std::enable_if_t<!boost::is_multi_array<Container>::value, decltype(flatten(std::declval<Container>().begin(), std::declval<Container>().end()))> begin() { return flatten(data.begin(), data.end()); } template <typename Container = T> typename std::enable_if_t<boost::is_multi_array<Container>::value, decltype(flatten(std::declval<typename Container::element_type*>(), std::declval<typename Container::element_type*>()))> begin() { // 把multi_array转换成连续的元素指针范围 return flatten(data.data(), data.data() + data.num_elements()); } template <typename Container = T> typename std::enable_if_t<!boost::is_multi_array<Container>::value, decltype(flatten(std::declval<Container>().end(), std::declval<Container>().end()))> end() { return flatten(data.end(), data.end()); } template <typename Container = T> typename std::enable_if_t<boost::is_multi_array<Container>::value, decltype(flatten(std::declval<typename Container::element_type*>() + std::declval<Container>().num_elements(), std::declval<typename Container::element_type*>() + std::declval<Container>().num_elements()))> end() { return flatten(data.data() + data.num_elements(), data.data() + data.num_elements()); }
完整测试代码
现在你的main函数里的c3可以正常编译运行了,测试代码如下:
int main() { auto c1 = NestedContainer<std::vector<std::vector<int>>, std::size_t>(3); // works auto c2 = NestedContainer<std::vector<std::list<int>>, std::size_t>(3); // works auto c3 = NestedContainer<boost::multi_array<std::list<int>, 2>, boost::multi_array_types::extent_gen::gen_type<2>::type>(boost::extents[2][2]); // now works std::cout << "c1 output: "; for ( auto &val: c1 ) { std::cout << val << " "; } std::cout << "\n"; std::cout << "c2 output: "; for ( auto &val: c2 ) { std::cout << val << " "; } std::cout << "\n"; std::cout << "c3 output: "; for ( auto &val: c3 ) { std::cout << val << " "; } std::cout << "\n"; }
运行后会输出所有嵌套结构里的int元素,完全符合你的需求。
备注:内容来源于stack exchange,提问作者Christoph90

