C++ 使用“分组”元素迭代向量?
以4种颜色包装在同一矢量中为例,设计的这一方面不容易改变-例如,来自第三方:C++ 使用“分组”元素迭代向量?,c++,range,iteration,c++17,C++,Range,Iteration,C++17,以4种颜色包装在同一矢量中为例,设计的这一方面不容易改变-例如,来自第三方: std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4}; 以下方法可以起作用: for (size_t ci = 0; ci + 2 < rgb_colors.size(); ci+=3) { auto& red_component = rgb_colors[ci]; auto& green_component = rgb_colors[ci
std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4};
以下方法可以起作用:
for (size_t ci = 0; ci + 2 < rgb_colors.size(); ci+=3) {
auto& red_component = rgb_colors[ci];
auto& green_component = rgb_colors[ci+1];
auto& blue_component = rgb_colors[ci+2];
//...
}
在真空中,这种方法是“无害的”。
然而,对于现代的c++/等,我通常会避开这种编码方法,因为它更脆弱/容易出错/冗余/等,现在更喜欢循环、迭代器等基于范围的编码
那么,解决这个问题的更具表现力或优雅的方式是什么呢
更新:
添加了一条关于“数据布局”不易更改的注释。如注释中所述,创建一个表示颜色的结构,或者更确切地说,使用您用于图形的任何库可能附带的结构,以便您可以正确地与其交互:
struct color {
int red, green, blue;
// Add members as you see fit
};
std::vector rgb_colors = {color{1,1,1}, color{2,2,2}, color{3,3,3}, color{4,4,4}};
// or
// std::vector<color> rgb_colors = {{1,1,1}, {2,2,2}, {3,3,3}, {4,4,4}};
for (auto& c : rgb_colors) {
auto& red_component = c.red;
auto& green_component = c.green;
auto& blue_component = c.blue;
//...
}
这仍然与您的示例一样,要求您确保向量的长度可被3整除,以避免UB,并且元素是std::vector首先提供的连续数组的一部分,以便指针算术&c[i]具有定义良好的行为。如注释中所述,创建一个表示颜色的结构,或者更确切地说,使用一个可能随图形库一起提供的结构,以便您可以正确地与它交互:
struct color {
int red, green, blue;
// Add members as you see fit
};
std::vector rgb_colors = {color{1,1,1}, color{2,2,2}, color{3,3,3}, color{4,4,4}};
// or
// std::vector<color> rgb_colors = {{1,1,1}, {2,2,2}, {3,3,3}, {4,4,4}};
for (auto& c : rgb_colors) {
auto& red_component = c.red;
auto& green_component = c.green;
auto& blue_component = c.blue;
//...
}
这仍然与您的示例一样,要求您确保向量的长度可被3整除,以避免UB,并且元素是std::vector首先提供的连续数组的一部分,以便指针算术&c[i]具有定义良好的行为。如注释中所述,可以更好地定义输入数据以匹配其含义,这将最好地避免问题 但是,如果您出于某种原因被一个序列困住了,其中N个元素一次意味着什么,并且您需要经常使用它,下面是我如何使用boost::iterator_Adapter创建一个迭代器,该迭代器一次捕获N个元素
#include <cstddef>
#include <utility>
#include <iterator>
#include <tuple>
#include <boost/iterator/iterator_adaptor.hpp>
namespace N_elements_iterator_detail {
template <typename T, std::size_t N>
using ignore_N = T;
template <typename T, typename IndSeq>
struct repeat_type_helper;
template <typename T, std::size_t ...Inds>
struct repeat_type_helper<T, std::index_sequence<Inds...>>
{ using type = std::tuple<ignore_N<T, Inds>...>; };
template <typename T, std::size_t N>
using repeat_type = typename repeat_type_helper<T, std::make_index_sequence<N>>::type;
template <typename IndSeq>
struct deref_helper;
template <std::size_t ...Inds>
struct deref_helper<std::index_sequence<Inds...>>
{
template <typename RetType, typename FwdIter>
static RetType deref(FwdIter iter) {
FwdIter iter_array[] =
{ (static_cast<void>(Inds), iter++) ... };
return RetType( *iter_array[Inds]... );
}
};
}
template <typename FwdIter,
typename std::iterator_traits<FwdIter>::difference_type N,
std::enable_if_t<(N>0)>* = nullptr>
class N_elements_iterator :
public boost::iterator_adaptor<
N_elements_iterator<FwdIter, N>, // CRTP Derived type
FwdIter, // Implementation base iterator
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::value_type, N>,
boost::use_default, // Iterator category
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::reference, N>>
{
public:
using N_elements_iterator::iterator_adaptor::iterator_adaptor;
private:
friend class boost::iterator_core_access;
typename N_elements_iterator::reference dereference() const {
return N_elements_iterator_detail::deref_helper<
std::make_index_sequence<N>>
::template deref<typename N_elements_iterator::reference>(
this->base());
}
void advance(typename N_elements_iterator::difference_type dist) {
std::advance(this->base_reference(), N * dist);
}
void increment() { advance(1); }
void decrement() { advance(-1); }
// N must be the same, but we can subtract for example
// N_elements_iterator<C::iterator, N> and N_elements_iterator<C::const_iterator, N>
template <typename OtherIter>
auto distance_to(const N_elements_iterator<OtherIter, N>& other) const {
return N * std::distance(this->base(), other.base());
}
};
template <auto N, typename FwdIter>
N_elements_iterator<FwdIter, N> make_N_elements_iterator(FwdIter iter)
{ return N_elements_iterator<FwdIter, N>{ iter }; }
// -----
#include <vector>
#include <iostream>
int main() {
std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4};
for (auto c = make_N_elements_iterator<3>(rgb_colors.cbegin());
c != make_N_elements_iterator<3>(rgb_colors.cend());
++c) {
auto&& [red, green, blue] = *c;
std::cout << "red " << red
<< ", green " << green
<< ", blue " << blue
<< '\n';
}
}
请参阅。如注释中所述,可以更好地定义输入数据以匹配其含义,这将最好地避免问题 但是,如果您出于某种原因被一个序列困住了,其中N个元素一次意味着什么,并且您需要经常使用它,下面是我如何使用boost::iterator_Adapter创建一个迭代器,该迭代器一次捕获N个元素
#include <cstddef>
#include <utility>
#include <iterator>
#include <tuple>
#include <boost/iterator/iterator_adaptor.hpp>
namespace N_elements_iterator_detail {
template <typename T, std::size_t N>
using ignore_N = T;
template <typename T, typename IndSeq>
struct repeat_type_helper;
template <typename T, std::size_t ...Inds>
struct repeat_type_helper<T, std::index_sequence<Inds...>>
{ using type = std::tuple<ignore_N<T, Inds>...>; };
template <typename T, std::size_t N>
using repeat_type = typename repeat_type_helper<T, std::make_index_sequence<N>>::type;
template <typename IndSeq>
struct deref_helper;
template <std::size_t ...Inds>
struct deref_helper<std::index_sequence<Inds...>>
{
template <typename RetType, typename FwdIter>
static RetType deref(FwdIter iter) {
FwdIter iter_array[] =
{ (static_cast<void>(Inds), iter++) ... };
return RetType( *iter_array[Inds]... );
}
};
}
template <typename FwdIter,
typename std::iterator_traits<FwdIter>::difference_type N,
std::enable_if_t<(N>0)>* = nullptr>
class N_elements_iterator :
public boost::iterator_adaptor<
N_elements_iterator<FwdIter, N>, // CRTP Derived type
FwdIter, // Implementation base iterator
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::value_type, N>,
boost::use_default, // Iterator category
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::reference, N>>
{
public:
using N_elements_iterator::iterator_adaptor::iterator_adaptor;
private:
friend class boost::iterator_core_access;
typename N_elements_iterator::reference dereference() const {
return N_elements_iterator_detail::deref_helper<
std::make_index_sequence<N>>
::template deref<typename N_elements_iterator::reference>(
this->base());
}
void advance(typename N_elements_iterator::difference_type dist) {
std::advance(this->base_reference(), N * dist);
}
void increment() { advance(1); }
void decrement() { advance(-1); }
// N must be the same, but we can subtract for example
// N_elements_iterator<C::iterator, N> and N_elements_iterator<C::const_iterator, N>
template <typename OtherIter>
auto distance_to(const N_elements_iterator<OtherIter, N>& other) const {
return N * std::distance(this->base(), other.base());
}
};
template <auto N, typename FwdIter>
N_elements_iterator<FwdIter, N> make_N_elements_iterator(FwdIter iter)
{ return N_elements_iterator<FwdIter, N>{ iter }; }
// -----
#include <vector>
#include <iostream>
int main() {
std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4};
for (auto c = make_N_elements_iterator<3>(rgb_colors.cbegin());
c != make_N_elements_iterator<3>(rgb_colors.cend());
++c) {
auto&& [red, green, blue] = *c;
std::cout << "red " << red
<< ", green " << green
<< ", blue " << blue
<< '\n';
}
}
请参阅。struct color{int red;int green;int blue;};向量rgb_颜色={1,1,1},{2,2},{3,3,3},{4,4,4};你能描述一下,你想要实现什么吗?我相信有一种方法可以创建一个迭代器适配器来检索向量的n,n+1和n+2元素,但是你最好还是按照@NathanOliver说的做这可能是一个很好的问题,如果你把它变成完整正确的代码,但在这里,它是纯粹基于观点的结构颜色{int-red;int-green;int-blue;};向量rgb_颜色={1,1,1},{2,2},{3,3,3},{4,4,4};你能描述一下,你想要实现什么吗?我相信有一种方法可以创建一个迭代器适配器来检索向量的n,n+1和n+2元素,但是你最好还是按照@NathanOliver说的做这可能是一个很好的问题,如果你把它变成完整正确的代码,但是这里的想法纯粹是正确的,但是用C++做这些事情是没有用的。如果是Windows,定义颜色和alpha的结构已经存在并建立。如果使用诸如OpenCV之类的库,则存在结构。在C++级上做它只是无UnC++,它本身没有GUI库,因此缺少任何这样定义的结构。这是C++ 20还是已经用C++ 17好?我的意思是:STD::向量RBGHORIZE =……@ ICDECV463035818 CTAD,在C++中引入17@NathanOliver天哪,我落后太多了。对于日常工作,我仍然必须使用C++11://@MichaelChourdakis是的,当然,应该使用库给定的类型,以便它能够正确地与它交互。我已经给答案加了一个注释。答案是正确的,但是用普通C++做这些事情是没有用的。如果是Windows,定义颜色和alpha的结构已经存在并建立。如果使用诸如OpenCV之类的库,则存在结构。在C++级上做它只是无UnC++,它本身没有GUI库,因此缺少任何这样定义的结构。这是C++ 20还是已经用C++ 17好?我的意思是:STD::向量RBGHORIZE =……@ ICDECV463035818 CTAD,在C++中引入17@NathanOliver天哪,我落后太多了。对于日常工作,我仍然必须使用C++11://@MichaelChourdakis是的,当然,应该使用库给定的类型,以便它能够正确地与它交互。我在回答中添加了一条评论。