我是"基于lambda的foreach循环的粉丝":
class SomeDataStructure
{
private:
std::vector<SomeData> data;
public:
template<typename TF> void forData(TF mFn)
{
for(int i{0}; i < data.size(); ++i)
mFn(i, data[i]);
}
};
SomeDataStructure sds;
int main()
{
sds.forData([](auto idx, auto& data)
{
// ...
});
}
我认为它对于更复杂的数据结构来说是一个很好的抽象,因为它允许用户使用其他参数直观地循环其内容。并且编译器优化应该保证性能等于传统的for(...)
循环。
不幸的是,使用这样的lambdas显然会阻止使用有时有用的continue;
和break;
语句。
sds.forData([](auto idx, auto& data)
{
// Not valid!
if(data.isInvalid()) continue;
});
有没有办法模拟continue;
和break;
语句而不会造成任何性能损失,并且不会使语法不方便?
答案 0 :(得分:9)
将成员函数forData
替换为生成迭代器的成员函数begin
和end
,然后替换
sds.forData([](auto idx, auto& data)
{
// Not valid!
if(data.isInvalid()) continue;
});
与
for( auto& data : sds )
{
if(data.isInvalid()) continue;
}
但是,如果您因某些未公开的原因而宁愿拥有forData
成员函数,那么您可以通过滥用异常来实现伪 - continue
和break
。例如,Python的for
循环基于异常。然后,forData
驱动程序代码将忽略continue-exception,并通过停止迭代来兑现break-exception。
template<typename TF> void forData(TF mFn)
{
for(int i{0}; i < data.size(); ++i)
{
try
{
mFn(i, data[i]);
}
catch( const Continue& )
{}
catch( const Break& )
{
return;
}
}
}
另一种方法是要求lambda返回一个值为&#34; break&#34;或者&#34;继续&#34;。
最自然的是使用枚举类型。
正如我所看到的,返回值方法的主要问题是它劫持了lambda结果值,例如:然后(非常容易)可以用它来产生循环累积的结果,或类似的东西。
for
循环,如本答案开头所建议的那样。但如果你这样做,并且你关注效率,那么请记住,首先要做的就是测量。
您可以实现类似Python的枚举函数,它可以生成集合的逻辑视图,因此集合看起来是(值,索引)对的集合,非常适合用于基于范围的for
循环:
cout << "Vector:" << endl;
vector<int> v = {100, 101, 102};
for( const auto e : enumerated( v ) )
{
cout << " " << e.item << " at " << e.index << endl;
}
以下代码(最低限度,仅为此答案拼凑而成)显示了一种方法:
#include <functional> // std::reference_wrapper
#include <iterator> // std::begin, std::end
#include <utility> // std::declval
#include <stddef.h> // ptrdiff_t
#include <type_traits> // std::remove_reference
namespace cppx {
using Size = ptrdiff_t;
using Index = Size;
template< class Type > using Reference = std::reference_wrapper<Type>;
using std::begin;
using std::declval;
using std::end;
using std::ref;
using std::remove_reference;
template< class Derived >
struct Rel_ops_from_compare
{
friend
auto operator!=( const Derived& a, const Derived& b )
-> bool
{ return compare( a, b ) != 0; }
friend
auto operator<( const Derived& a, const Derived& b )
-> bool
{ return compare( a, b ) < 0; }
friend
auto operator<=( const Derived& a, const Derived& b )
-> bool
{ return compare( a, b ) <= 0; }
friend
auto operator==( const Derived& a, const Derived& b )
-> bool
{ return compare( a, b ) == 0; }
friend
auto operator>=( const Derived& a, const Derived& b )
-> bool
{ return compare( a, b ) >= 0; }
friend
auto operator>( const Derived& a, const Derived& b )
-> bool
{ return compare( a, b ) > 0; }
};
template< class Type >
struct Index_and_item
{
Index index;
Reference<Type> item;
};
template< class Iterator >
class Enumerator
: public Rel_ops_from_compare< Enumerator< Iterator > >
{
private:
Iterator it_;
Index index_;
public:
using Referent = typename remove_reference<
decltype( *declval<Iterator>() )
>::type;
friend
auto compare( const Enumerator& a, const Enumerator& b )
-> Index
{ return a.index_ - b.index_; }
auto operator->() const
-> Index_and_item< Referent >
{ return Index_and_item< Referent >{ index_, ref( *it_ )}; }
auto operator*() const
-> Index_and_item< Referent >
{ return Index_and_item< Referent >{ index_, ref( *it_ )}; }
Enumerator( const Iterator& it, const Index index )
: it_( it ), index_( index )
{}
auto operator++()
-> Enumerator&
{ ++it_; ++index_; return *this; }
auto operator++( int )
-> Enumerator
{
const Enumerator result = *this;
++*this;
return result;
}
auto operator--()
-> Enumerator&
{ --it_; --index_; return *this; }
auto operator--( int )
-> Enumerator
{
const Enumerator result = *this;
--*this;
return result;
}
};
template< class Collection >
struct Itertype_for_ { using T = typename Collection::iterator; };
template< class Collection >
struct Itertype_for_<const Collection> { using T = typename Collection::const_iterator; };
template< class Type, Size n >
struct Itertype_for_< Type[n] > { using T = Type*; };
template< class Type, Size n >
struct Itertype_for_< const Type[n] > { using T = const Type*; };
template< class Collection >
using Itertype_for = typename Itertype_for_< typename remove_reference< Collection >::type >::T;
template< class Collection >
class Enumerated
{
private:
Collection& c_;
public:
using Iter = Itertype_for< Collection >;
using Eter = Enumerator<Iter>;
auto begin() -> Eter { return Eter( std::begin( c_ ), 0 ); }
auto end() -> Eter { return Eter( std::end( c_ ), std::end( c_ ) - std::begin( c_ ) ); }
//auto cbegin() const -> decltype( c_.cbegin() ) { return c_.cbegin(); }
//auto cend() const -> decltype( c_.cend() ) { return c_.cend(); }
Enumerated( Collection& c )
: c_( c )
{}
};
template< class Collection >
auto enumerated( Collection& c )
-> Enumerated< Collection >
{ return Enumerated<Collection>( c ); }
} // namespace cppx
#include <iostream>
#include <vector>
using namespace std;
auto main() -> int
{
using cppx::enumerated;
cout << "Vector:" << endl;
vector<int> v = {100, 101, 102};
for( const auto e : enumerated( v ) )
{
cout << " " << e.item << " at " << e.index << endl;
}
cout << "Array:" << endl;
int a[] = {100, 101, 102};
for( const auto e : enumerated( a ) )
{
cout << " " << e.item << " at " << e.index << endl;
}
}
答案 1 :(得分:7)
声明你的forData
需要一个返回布尔值的lambda。当lambda返回true
时,请退出for循环。
然后在lambda中使用return true;
代表break;
,return false;
代表continue;
如果您不需要中断功能,就像在上一个示例中一样,只需将continue
替换为return
就足够了......