如何使用 c++11 语法创建 for 循环以遍历向量
How to create a for loop to go through vectors using the c++11 syntax
我有一个 class
,其中包含一些 vectors
(此处为最少代码):
class Foo
{
public:
Foo() = default;
private:
void DoStuff();
std::vector<int>& mBarInt;
std::vector<double> mBarDouble;
int do_something_with_int;
double do_something_with_double;
};
在我的 DoStuff()
方法中,我有:
void Foo::DoStuff()
{
for(auto &val : mBarInt)
{
// do something here...
do_something_with_int = val + ....
}
}
我想做这样的事情,在同一个 for
上访问两个向量,但是(见下文代码)...
for(int i = 0 ; i < mBarInt.size() ; i++)
{
// do something here...
do_something_with_int = mBarInt[i] + ....
do_something_with_double = mBarDouble[i] + .... // I know mBarDouble has the same size has mBarInt
}
... 保持与以下相同的 c++11 语法:for(auto &val : mBarInt)
如下所示:
void Foo::DoStuff()
{
for(auto &val1, auto val2 : mBarInt, mBarDouble) //?????<---- How can I do this?
{
// do something with val1 and val2, which I know that will point to different vectors, but on the same index.
}
}
template<class It>
struct indexing_iterator {
It it = {};
using value_type = It;
using reference = value_type;
It operator*() const { return it; }
indexing_iterator& operator++() { ++it; return *this; }
friend bool operator==( indexing_iterator const& lhs, indexing_iterator const& rhs ) {
return lhs.it == rhs.it;
}
friend bool operator!=( indexing_iterator const& lhs, indexing_iterator const& rhs ) {
return !(lhs==rhs);
}
};
template<class F, class It_in>
struct transform_iterator_t:indexing_iterator<It_in> {
F f = {};
using reference = decltype(std::declval<F&>()( *std::declval<It_in&>() ) );
using value_type = std::decay_t<reference>;
using pointer = value_type*;
reference operator*() { return f(*this->it); }
transform_iterator_t( F f_in, It_in it_in ):
indexing_iterator<It_in>{it_in},
f(f_in)
{}
};
template<class F, class It_in>
transform_iterator_t<F,It_in> transform_iterator(
F f, It_in it
) {
return {f, it};
}
indexing_iterator<std::size_t> index( std::size_t n ) {
return {n};
}
template<class It>
struct range_t {
It b,e;
It begin() const { return b; }
It end() const { return b; }
};
template<class It>
range_t<It> range( It b, It e ) { return {b,e}; }
auto range_upto( std::size_t n ) {
return range( index(0), index(n) );
}
template<class C>
auto to_range( C&& c ) {
using std::begin; using std::end;
return range( begin(c), end(c) );
}
template<class It, class F>
range_t< transform_iterator< F, It > >
transform_range( F f, range_t<It> range ) {
return {
transform_iterator( f, range.begin() ),
transform_iterator( f, range.end() )
};
}
template<class F>
auto generator_range( F f, std::size_t count ) {
return transform_range( f, range_upto(count) );
}
template<class C1, class C2>
auto zip_over( C1& c1, C2& c2 ) {
return generator_range(
[&](std::size_t i) {
return std::tie( c1[i], c2[i] );
},
(std::min)(c1.size(), c2.size())
);
}
然后在 C++17 中我们有
void Foo::DoStuff() {
for(auto&&[val1, val2] : zip_over(mBarInt, mBarDouble)) {
}
}
或者您可以在 C++14 中手动解压元组。
看起来工作量很大。
boost
和其他库,包括Rangev3.
中有zip之类的实现
代码未经测试。设计应该是合理的。
可能有更短的方法可以更直接地做到这一点,但我发现通过转换和索引生成器,通过生成器压缩,更容易理解和编写,错误更少。
一个 indexing_iterator
接受一个可递增的可比较对象,并对其进行迭代。 It
可以是类整数或整数。
A transform_iterator
采用迭代器和转换,returns 每个元素的转换。
全迭代器也不是;我跳过了样板。但它们足以 for(:)
循环。
A range
持有 2 个迭代器并将它们公开给 for(:)
.
indexing_iterator<std::size_t>
也称为计数迭代器。
生成器迭代器是计数迭代器的转换。它接受一个接受索引的函数,并为该索引生成一个元素。
我们通过生成一个生成器迭代器压缩两个随机访问容器,该迭代器在两个容器上调用 operator[]
,然后 returns 一个元组。更高级的压缩处理非随机访问容器。
然后,两个容器的 zip 允许您在两个不同的容器上并行迭代。
然后我在 for(:)
语句中使用 C++17 结构化绑定将它们解压缩到两个不同的变量中。我们可以改为手动解包作为 for 循环中的前 2 个语句。
您可以像这样使用 boost::make_iterator_range
和 boost::zip_iterator
:
std::vector<int> ints{1,2,3,4,5};
std::vector<double> doubles{1,2,3,4,5.1};
int do_something_with_int{0};
double do_something_with_double{0};
for (const auto& ref :
boost::make_iterator_range(
boost::make_zip_iterator(boost::make_tuple(ints.cbegin(), doubles.cbegin())),
boost::make_zip_iterator(boost::make_tuple(ints.cend(), doubles.cend()))))
{
do_something_with_int += boost::get<0>(ref);
do_something_with_double+= boost::get<1>(ref);
}
我有一个 class
,其中包含一些 vectors
(此处为最少代码):
class Foo
{
public:
Foo() = default;
private:
void DoStuff();
std::vector<int>& mBarInt;
std::vector<double> mBarDouble;
int do_something_with_int;
double do_something_with_double;
};
在我的 DoStuff()
方法中,我有:
void Foo::DoStuff()
{
for(auto &val : mBarInt)
{
// do something here...
do_something_with_int = val + ....
}
}
我想做这样的事情,在同一个 for
上访问两个向量,但是(见下文代码)...
for(int i = 0 ; i < mBarInt.size() ; i++)
{
// do something here...
do_something_with_int = mBarInt[i] + ....
do_something_with_double = mBarDouble[i] + .... // I know mBarDouble has the same size has mBarInt
}
... 保持与以下相同的 c++11 语法:for(auto &val : mBarInt)
如下所示:
void Foo::DoStuff()
{
for(auto &val1, auto val2 : mBarInt, mBarDouble) //?????<---- How can I do this?
{
// do something with val1 and val2, which I know that will point to different vectors, but on the same index.
}
}
template<class It>
struct indexing_iterator {
It it = {};
using value_type = It;
using reference = value_type;
It operator*() const { return it; }
indexing_iterator& operator++() { ++it; return *this; }
friend bool operator==( indexing_iterator const& lhs, indexing_iterator const& rhs ) {
return lhs.it == rhs.it;
}
friend bool operator!=( indexing_iterator const& lhs, indexing_iterator const& rhs ) {
return !(lhs==rhs);
}
};
template<class F, class It_in>
struct transform_iterator_t:indexing_iterator<It_in> {
F f = {};
using reference = decltype(std::declval<F&>()( *std::declval<It_in&>() ) );
using value_type = std::decay_t<reference>;
using pointer = value_type*;
reference operator*() { return f(*this->it); }
transform_iterator_t( F f_in, It_in it_in ):
indexing_iterator<It_in>{it_in},
f(f_in)
{}
};
template<class F, class It_in>
transform_iterator_t<F,It_in> transform_iterator(
F f, It_in it
) {
return {f, it};
}
indexing_iterator<std::size_t> index( std::size_t n ) {
return {n};
}
template<class It>
struct range_t {
It b,e;
It begin() const { return b; }
It end() const { return b; }
};
template<class It>
range_t<It> range( It b, It e ) { return {b,e}; }
auto range_upto( std::size_t n ) {
return range( index(0), index(n) );
}
template<class C>
auto to_range( C&& c ) {
using std::begin; using std::end;
return range( begin(c), end(c) );
}
template<class It, class F>
range_t< transform_iterator< F, It > >
transform_range( F f, range_t<It> range ) {
return {
transform_iterator( f, range.begin() ),
transform_iterator( f, range.end() )
};
}
template<class F>
auto generator_range( F f, std::size_t count ) {
return transform_range( f, range_upto(count) );
}
template<class C1, class C2>
auto zip_over( C1& c1, C2& c2 ) {
return generator_range(
[&](std::size_t i) {
return std::tie( c1[i], c2[i] );
},
(std::min)(c1.size(), c2.size())
);
}
然后在 C++17 中我们有
void Foo::DoStuff() {
for(auto&&[val1, val2] : zip_over(mBarInt, mBarDouble)) {
}
}
或者您可以在 C++14 中手动解压元组。
看起来工作量很大。
boost
和其他库,包括Rangev3.
代码未经测试。设计应该是合理的。
可能有更短的方法可以更直接地做到这一点,但我发现通过转换和索引生成器,通过生成器压缩,更容易理解和编写,错误更少。
一个 indexing_iterator
接受一个可递增的可比较对象,并对其进行迭代。 It
可以是类整数或整数。
A transform_iterator
采用迭代器和转换,returns 每个元素的转换。
全迭代器也不是;我跳过了样板。但它们足以 for(:)
循环。
A range
持有 2 个迭代器并将它们公开给 for(:)
.
indexing_iterator<std::size_t>
也称为计数迭代器。
生成器迭代器是计数迭代器的转换。它接受一个接受索引的函数,并为该索引生成一个元素。
我们通过生成一个生成器迭代器压缩两个随机访问容器,该迭代器在两个容器上调用 operator[]
,然后 returns 一个元组。更高级的压缩处理非随机访问容器。
然后,两个容器的 zip 允许您在两个不同的容器上并行迭代。
然后我在 for(:)
语句中使用 C++17 结构化绑定将它们解压缩到两个不同的变量中。我们可以改为手动解包作为 for 循环中的前 2 个语句。
您可以像这样使用 boost::make_iterator_range
和 boost::zip_iterator
:
std::vector<int> ints{1,2,3,4,5};
std::vector<double> doubles{1,2,3,4,5.1};
int do_something_with_int{0};
double do_something_with_double{0};
for (const auto& ref :
boost::make_iterator_range(
boost::make_zip_iterator(boost::make_tuple(ints.cbegin(), doubles.cbegin())),
boost::make_zip_iterator(boost::make_tuple(ints.cend(), doubles.cend()))))
{
do_something_with_int += boost::get<0>(ref);
do_something_with_double+= boost::get<1>(ref);
}