foreach.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 519 行 · 第 1/2 页

QBK
519
字号
    }Now that we have taught _range_ (and hence _foreach_) about our type, wecan now use _foreach_ to iterate over our sub_string type.    my::sub_string substr;    BOOST_FOREACH( char ch, substr )    {        // Woo-hoo!    }There are some portability issues we should be aware of when extending _foreach_. Be sureto check out the [link foreach.portability Portability] section. In particular, if yourcompiler does not support Argument-Dependent Lookup, the _range_portability_ sectionoffers some suggested work-arounds.[h2 Making _foreach_ Work with Non-Copyable Sequence Types]For sequence types that are non-copyable, we will need to tell _foreach_ tonot try to make copies. If our type inherits from _noncopyable_, no further action isrequired. If not, we must specialize the `boost::foreach::is_noncopyable<>` template, asfollows:    class noncopy_vector    {        // ...    private:        noncopy_vector( noncopy_vector const & ); // non-copyable!    };        namespace boost { namespace foreach    {        template<>        struct is_noncopyable< noncopy_vector >          : mpl::true_        {        };    }}Another way to achieve the same effect is to override the global `boost_foreach_is_noncopyable()`function. Doing it this way has the advantage of being portable to older compilers.    // At global scope...    inline boost::mpl::true_ *    boost_foreach_is_noncopyable( noncopy_vector *&, boost::foreach::tag )    {        return 0;    }[tip Even though we have to tell _foreach_ that our type is non-copyable, thatdoesn't mean that _foreach_ always makes a copy of our sequence type. Obviously, doing sowould be expensive and even wrong in some cases. _foreach_ is quite smart about when tomake a copy and when not to. The `is_noncopyable<>` trait is needed to elide the copy, whichis on a branch that might never get taken.][h2 Optimizing _foreach_ for Lightweight Proxy Sequence Types]On some compilers, _foreach_ must occasionally take a slightly slower code path to guaranteecorrect handling of sequences stored in temporary objects. It asks itself, "Should I makea copy of this object?" and later, "Did I make a copy or not?" For some types of sequences,this is overkill. Consider a sequence which is a simple pair of iterators. Jumping throughhoops of fire to avoid copying it doesn't make sense because copying it is so cheap.A pair of iterators is an example of a lightweight proxy. It does not store the values ofthe sequence; rather, it stores iterators to them. This means that iterating over a copy ofthe proxy object will give the same results as using the object itself. For such types,_foreach_ provides a hook that lets us tell it not to worry about the expense of making acopy. This can result in slightly faster loop execution. Simply specialize the`boost::foreach::is_lightweight_proxy<>` trait, as follows:    struct sub_string      : boost::iterator_range< std::string::iterator >    {        // ...    };        namespace boost { namespace foreach    {        template<>        struct is_lightweight_proxy< sub_string >          : mpl::true_        {        };    }}Alternately, we could achieve the same effect by overriding the global`boost_foreach_is_lightweight_proxy()` function, as follows:    // At global scope...    inline boost::mpl::true_ *    boost_foreach_is_lightweight_proxy( sub_string *&, boost::foreach::tag )    {        return 0;    }This method is portable to older compilers.[endsect][section Portability]_foreach_ uses some fairly sophisticated techniques that not all compilers support. Dependingon how compliant your compiler is, you may not be able to use _foreach_ in some scenarios. Since_foreach_ uses _range_, it inherits _range_'s portability issues. You can read about thoseissues in the _range_portability_ section.In addition to the demands placed on the compiler by _range_, _foreach_ places additional demandsin order to handle rvalue sequences properly. (Recall that an rvalue is an unnamed object, soan example of an rvalue sequence would be a function that returns a `std::vector<>` by value.) Compilersvary in their handling of rvalues and lvalues. To cope with the situation _foreach_ defines threelevels of compliance, described below:[table BOOST_FOREACH Compliance Levels  [[Level]     [Meaning]]  [[*Level 0*] [['[_Highest level of compliance]]\n                _foreach_ works with lvalues, rvalues and const-qualified rvalues.]]  [[*Level 1*] [['[_Moderate level of compliance]]\n                _foreach_ works with lvalues and plain rvalues, but not const-qualified rvalues.\n                `BOOST_FOREACH_NO_CONST_RVALUE_DETECTION` is defined in this case.]]  [[*Level 2*] [['[_Lowest level of compliance]]\n                _foreach_ works with lvalues only, not rvalues.\n                `BOOST_FOREACH_NO_RVALUE_DETECTION` is defined in this case.]]]Below are the compilers with which _foreach_ has been tested, and the compliance level _foreach_provides for them.[table Compiler Compliance Level  [[Compiler]                [Compliance Level]]  [[Visual C++ 8.0]          [Level 0]]  [[Visual C++ 7.1]          [Level 0]]  [[Visual C++ 7.0]          [Level 2]]  [[Visual C++ 6.0]          [Level 2]]  [[gcc 4.0]                 [Level 0]]  [[gcc 3.4]                 [Level 0]]  [[gcc 3.3]                 [Level 0]]  [[mingw 3.4]               [Level 0]]  [[Intel for Linux 9.0]     [Level 0]]  [[Intel for Windows 9.0]   [Level 0]]  [[Intel for Windows 8.0]   [Level 1]]  [[Intel for Windows 7.0]   [Level 2]]  [[Comeau 4.3.3]            [Level 0]]  [[Borland 5.6.4]           [Level 2]]  [[Metrowerks 9.5]          [Level 1]]  [[Metrowerks 9.4]          [Level 1]]  [[SunPro 5.8]              [Level 2]]  [[qcc 3.3]                 [Level 0]]  [[tru64cxx 65]             [Level 2]]  [[tru64cxx 71]             [Level 2]]][endsect][section Pitfalls]This section describes some common pitfalls with _foreach_.[h2 Types With Commas]Since _foreach_ is a macro, it must have exactly two arguments, with exactly onecomma separating them. That's not always convenient, especially when the type of theloop variable is a template. Consider trying to iterate over a `std::map`:    std::map<int,int> m;    // ERROR! Too many arguments to BOOST_FOREACH macro.    BOOST_FOREACH(std::pair<int,int> p, m) // ...One way to fix this is with a typedef.    std::map<int,int> m;    typedef std::pair<int,int> pair_t;    BOOST_FOREACH(pair_t p, m) // ...Another way to fix it is to predeclare the loop variable:    std::map<int,int> m;    std::pair<int,int> p;    BOOST_FOREACH(p, m) // ...[h2 Hoisting and Iterator Invalidation]Under the covers, _foreach_ uses iterators to traverse the elementsequence. Before the loop is executed, the end iterator is cachedin a local variable. This is called ['hoisting], and it is animportant optimization. It assumes, however, that the end iteratorof the sequence is stable. It usually is, but if we modify thesequence by adding or removing elements while we are iteratingover it, we may end up hoisting ourselves on our own petard.Consider the following code:    std::vector<int> vect(4, 4);    BOOST_FOREACH(int i, vect)    {        vect.push_back(i + 1);    }This code will compile, but it has undefined behavior. That is becauseit is logically equivalent to the following:    std::vector<int> vect(4, 4);    for(std::vector<int>::iterator it1 = vect.begin(), it2 = vect.end();        it1 != it2; ++it1)    {        int i = *it1;        vect.push_back(i + 1); // Oops! This invalidates it1 and it2!    }The call to `vect.push_back()` will cause all iterators into `vect` tobecome invalid, including `it1` and `it2`. The next iteration throughthe loop will cause the invalid iterators to be used. That's bad news.The moral of the story is to think twice before adding and removingelements from the sequence over which you are iterating. If doingso could cause iterators to become invalid, don't do it. Use a regular`for` loop instead.[endsect][section History and Acknowledgements][h2 History]The ideas for _foreach_ began life in the Visual C++ group at Microsoft during the early phases ofthe design for C++/CLI. Whether to add a dedicated "foreach" looping construct to the language wasan open question at the time. As a mental exercise, Anson Tsao sent around some proof-of-conceptcode which demonstrated that a pure library solution might be possible. The code was written in theproposed C++/CLI dialect of the time, for which there was no compiler as of yet. I was intrigued bythe possibility, and I ported his code to Managed C++ and got it working. We worked together torefine the idea and eventually published an article about it in the November 2003 issue of the[@http://www.cuj.com CUJ].After leaving Microsoft, I revisited the idea of a looping construct. I reimplemented the macrofrom scratch in standard C++, corrected some shortcomings of the CUJ version and rechristened it_foreach_. In October of 2003 I began a discussion about it on the Boost developers list, whereit met with a luke-warm reception. I dropped the issue until December 2004, when I reimplemented_foreach_ yet again. The new version only evaluated its sequence expression once and correctlyhandled both lvalue and rvalue sequence expressions. It was built on top of the recentlyaccepted _range_ library, which increased its portability. This was the version that, on Dec. 12 2004,I finally submitted to Boost for review. It was accepted into Boost on May 5, 2005.[h2 Acknowledgements]Thanks go out to Anson Tsao of Microsoft for coming up with the idea and demonstrating its feasibility.I would also like to thank [@http://boost.org/people/thorsten_ottosen.html Thorsten Ottosen] forthe _range_ library, on which the current version of _foreach_ is built. Finally, I'd like to thankRussell Hind, Alisdair Meredith and Stefan Slapeta for their help porting to various compilers.[h2 Further Reading]For more information about how _foreach_ works, you may refer to the article[@http://www.artima.com/cppsource/foreach.html ["Conditional Love]] at[@http://www.artima.com/cppsource/ The C++ Source].[endsect]

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?