我是“基于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) { // ... }); }
我认为对于更复杂的数据结构来说,这是一个很好的抽象,因为它允许用户直观地循环使用其他参数的内容.并且编译器优化应保证性能等于传统的(…)循环.
不幸的是,使用这样的lambdas显然阻止了使用有时有用的继续;打破声明.
sds.forData([](auto idx,auto& data) { // Not valid! if(data.isInvalid()) continue; });
有没有办法模拟继续;打破语句没有任何性能损失,而不会使语法更不方便?
解决方法
用成员函数begin和end替换成员函数forData,生成迭代器,然后替换
sds.forData([](auto idx,auto& data) { // Not valid! if(data.isInvalid()) continue; });
同
for( auto& data : sds ) { if(data.isInvalid()) continue; }
但是,如果你有一些未公开的理由会使用forData成员函数,那么你可以通过一些滥用异常来实现伪继承和中断.例如,Python的for循环基于一个异常.然后,forData驱动程序代码将忽略继续异常,并通过停止迭代来遵守断点异常.
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返回一个表示“break”或“continue”的值.
最自然的是为此使用枚举类型.
我看到的返回值方法的主要问题是它劫持lambda结果值,例如它不能(很容易)用于产生由循环积累的结果,或任何类似的结果.
我不会这样做.我宁愿使用基于范围的for循环,如本答案开始时所推荐的那样.但是,如果你这样做,并且关心效率,那么请记住,首先要做的是衡量.
您可以实现一个类似Python的枚举函数,该函数产生一个集合的逻辑视图,以便集合看起来是一个(值,索引)对的集合,非常适合在基于范围的循环中使用:
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,b ) < 0; } friend auto operator<=( const Derived& a,b ) <= 0; } friend auto operator==( const Derived& a,b ) == 0; } friend auto operator>=( const Derived& a,b ) >= 0; } friend auto operator>( const Derived& 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,102}; for( const auto e : enumerated( v ) ) { cout << " " << e.item << " at " << e.index << endl; } cout << "Array:" << endl; int a[] = {100,102}; for( const auto e : enumerated( a ) ) { cout << " " << e.item << " at " << e.index << endl; } }