Boost C++ Libraries: Ticket #9812: BinaryOperation boost::transform only stops when it reaches the end of rng1, even if rng2 is shorter https://svn.boost.org/trac10/ticket/9812 <p> Here's a complete program that reproduces the problem: </p> <p> #include &lt;boost/range/algorithm/transform.hpp&gt; #include &lt;boost/range/irange.hpp&gt; </p> <p> namespace std { </p> <blockquote> <p> ostream&amp; operator&lt;&lt;(ostream&amp; stream, const pair&lt;int, int&gt;&amp; p) { </p> <blockquote> <p> return stream &lt;&lt; "(" &lt;&lt; p.first &lt;&lt; ", " &lt;&lt; p.second &lt;&lt; ")"; </p> </blockquote> <p> } </p> </blockquote> <p> } </p> <p> int main() { </p> <blockquote> <p> boost::transform( </p> <blockquote> <p> boost::irange(0, 100), boost::irange(0, 10), std::ostream_iterator&lt;std::pair&lt;int,int&gt;&gt;(std::cout, "\n"), [](const int left, const int right) { return std::make_pair(left, right); }); </p> </blockquote> <p> return EXIT_SUCCESS; </p> </blockquote> <p> } </p> <p> I compile on Fedora 20 using g++ 4.8.2. Here's the output I get: </p> <p> g++ transform.cpp -std=c++11 &amp;&amp; ./a.out (0, 0) (1, 1) (2, 2) (3, 3) (4, 4) (5, 5) (6, 6) (7, 7) (8, 8) (9, 9) a.out: /usr/include/boost/range/algorithm/transform.hpp:63: <a class="missing wiki">OutputIterator</a> boost::range_detail::transform_impl(SinglePassTraversalReadableIterator1, SinglePassTraversalReadableIterator1, SinglePassTraversalReadableIterator2, SinglePassTraversalReadableIterator2, <a class="missing wiki">OutputIterator</a>, <a class="missing wiki">BinaryFunction</a>) [with SinglePassTraversalReadableIterator1 = boost::range_detail::integer_iterator&lt;int&gt;; SinglePassTraversalReadableIterator2 = boost::range_detail::integer_iterator&lt;int&gt;; <a class="missing wiki">OutputIterator</a> = std::ostream_iterator&lt;std::pair&lt;int, int&gt; &gt;; <a class="missing wiki">BinaryFunction</a> = main()::<span class="underline">lambda0]: Assertion `first2 != last2' failed. Abort </span></p> <p> Here's the code I believe to be broken in boost/range/algorithm/transform.hpp: </p> <blockquote> <p> template&lt; class SinglePassTraversalReadableIterator1, </p> <blockquote> <p> class SinglePassTraversalReadableIterator2, class <a class="missing wiki">OutputIterator</a>, class <a class="missing wiki">BinaryFunction</a> &gt; </p> </blockquote> <p> inline <a class="missing wiki">OutputIterator</a> transform_impl(SinglePassTraversalReadableIterator1 first1, </p> <blockquote> <blockquote> <p> SinglePassTraversalReadableIterator1 last1, SinglePassTraversalReadableIterator2 first2, SinglePassTraversalReadableIterator2 last2, <a class="missing wiki">OutputIterator</a> out, <a class="missing wiki">BinaryFunction</a> fn) </p> </blockquote> </blockquote> <p> { </p> <blockquote> <p> for (; first1 != last1; ++first1, ++first2) { </p> <blockquote> <p> BOOST_ASSERT( first2 != last2 ); *out = fn(*first1, *first2); ++out; </p> </blockquote> <p> } return out; </p> </blockquote> <p> } </p> </blockquote> <p> Here's my proposed fix: </p> <blockquote> <p> template&lt; class SinglePassTraversalReadableIterator1, </p> <blockquote> <p> class SinglePassTraversalReadableIterator2, class <a class="missing wiki">OutputIterator</a>, class <a class="missing wiki">BinaryFunction</a> &gt; </p> </blockquote> <p> inline <a class="missing wiki">OutputIterator</a> transform_impl(SinglePassTraversalReadableIterator1 first1, </p> <blockquote> <blockquote> <p> SinglePassTraversalReadableIterator1 last1, SinglePassTraversalReadableIterator2 first2, SinglePassTraversalReadableIterator2 last2, <a class="missing wiki">OutputIterator</a> out, <a class="missing wiki">BinaryFunction</a> fn) </p> </blockquote> </blockquote> <p> { </p> <blockquote> <p> for (; first1 != last1 &amp;&amp; first2 != last2; ++first1, ++first2) { </p> <blockquote> <p> *out = fn(*first1, *first2); ++out; </p> </blockquote> <p> } return out; </p> </blockquote> <p> } </p> </blockquote> <p> I added a check that first2 != last2 and removed the ASSERT. </p> <p> Here's the relevant documentation: <a class="missing wiki">BinaryOperation</a> version: </p> <p> transform assigns the value z to each element [out, out + min(distance(rng1), distance(rng2))), z = fun(x,y) where x is the corresponding value in rng1 and y is the corresponding value in rng2. This version of transform stops upon reaching either the end of rng1, or the end of rng2. Hence there isn't a requirement for distance(rng1) == distance(rng2) since there is a safe guaranteed behaviour, unlike with the iterator counterpart in the standard library. </p> <hr /> <p> Please note that the original implementation doesn't meet the specification described above, whereas the new implementation does. <a class="missing wiki">BinaryOperation</a> boost::for_each has a similar specification, but it implements it correctly. </p> <p> Boost Rocks! Keep up the stellar work! </p> <p> Don't hesitate to contact me if any of the above needs further explanation. </p> <p> Thanks, David </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/9812 Trac 1.4.3 Neil Groves Wed, 07 May 2014 00:09:11 GMT status, milestone changed https://svn.boost.org/trac10/ticket/9812#comment:1 https://svn.boost.org/trac10/ticket/9812#comment:1 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">assigned</span> </li> <li><strong>milestone</strong> <span class="trac-field-old">To Be Determined</span> → <span class="trac-field-new">Boost 1.57.0</span> </li> </ul> <p> This is fixed on the "develop" branch. </p> Ticket anonymous Wed, 04 Jun 2014 11:33:04 GMT milestone changed https://svn.boost.org/trac10/ticket/9812#comment:2 https://svn.boost.org/trac10/ticket/9812#comment:2 <ul> <li><strong>milestone</strong> <span class="trac-field-old">Boost 1.57.0</span> → <span class="trac-field-new">Boost 1.56.0</span> </li> </ul> <p> Release window for 1.56 has been extended so I can put this into 1.56. </p> Ticket Neil Groves Wed, 04 Jun 2014 17:39:01 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/9812#comment:3 https://svn.boost.org/trac10/ticket/9812#comment:3 <ul> <li><strong>status</strong> <span class="trac-field-old">assigned</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> Merged to master and ready for the next release. </p> Ticket