Boost C++ Libraries: Ticket #9449: Boost.MPL min_element bug for equal elements (fix included) https://svn.boost.org/trac10/ticket/9449 <p> For equal elements, Boost.MPL <code>min_element</code> with a predicate <code>Pred</code> does not satisfy its own requirements of returning the <strong>first</strong> element <code>i</code> that satifies <code>!Pred(j, i)</code> for all <code>j</code>. It turns out that <code>min_element</code> is implemented in terms of <code>max_element</code> with the negated predicate <code>not_&lt;Pred&gt;</code>. In turn, <code>max_element</code> with its own predicate <code>Pred</code> is required to (and in fact does) return the first element <code>i</code> that satisfies <code>!Pred(i, j)</code>. </p> <p> It is straightforward to see that negating the predicate is a bug. The current implementation of <code>min_element</code> has <code>less</code> as its default predicate, and subsequently calls <code>max_element</code> with <code>greater_equal</code>, whereas the requirements indicate that it should use <code>greater</code>. This results in <code>min_element</code> returning the <strong>last</strong> element of the sequence, rather than the first. </p> <p> To get the required semantics, users currently have to supply <code>less_equal</code> to <code>min_element</code> as a workaround. The proper fix would be to implement <code>min_element</code> by calling <code>max_element</code> with the arguments for the predicate reversed: <code>lambda&lt;Pred, _2, _1&gt;</code>. </p> <p> See below for a short example that displays the problem and implements the fix. The program sets up a <code>vector</code> of three equal elements. Both <code>min_element</code> and <code>max_element</code> are specified to return an iterator to the first (index 0) element. Instead, <code>min_element</code> returns an iterator to the last (index 2) element. Both the redefined predicate work-around and the reimplementation of <code>min_element</code> fix the problem. The example can be run from <a class="ext-link" href="http://coliru.stacked-crooked.com/a/c517d85cbc0aee66"><span class="icon">​</span>http://coliru.stacked-crooked.com/a/c517d85cbc0aee66</a> </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/begin_end.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/distance.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/lambda.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/less_equal.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/max_element.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/min_element.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/placeholders.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/vector_c.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp"></span> <span class="k">using</span> <span class="k">namespace</span> <span class="n">boost</span><span class="o">::</span><span class="n">mpl</span><span class="p">;</span> <span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">Sequence</span><span class="p">,</span> <span class="k">class</span> <span class="nc">Predicate</span> <span class="o">=</span> <span class="n">less</span><span class="o">&lt;</span><span class="n">_1</span><span class="p">,</span><span class="n">_2</span><span class="o">&gt;&gt;</span> <span class="k">struct</span> <span class="nl">stable_min_element</span> <span class="p">:</span> <span class="c1">// mpl::min_element uses max_element&lt;Sequence, not_&lt;Predicate&gt;&gt;</span> <span class="n">max_element</span><span class="o">&lt;</span><span class="n">Sequence</span><span class="p">,</span> <span class="n">lambda</span><span class="o">&lt;</span><span class="n">Predicate</span><span class="p">,</span> <span class="n">_2</span><span class="p">,</span> <span class="n">_1</span><span class="o">&gt;&gt;</span> <span class="p">{};</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="k">using</span> <span class="n">V</span> <span class="o">=</span> <span class="n">vector_c</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">MinIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">min_element</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">LEMinIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">min_element</span><span class="o">&lt;</span><span class="n">V</span><span class="p">,</span> <span class="n">less_equal</span><span class="o">&lt;</span><span class="n">_1</span><span class="p">,</span> <span class="n">_2</span><span class="o">&gt;</span> <span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">SMinIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">stable_min_element</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">MaxIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">max_element</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">MinIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// ERROR: prints 2 instead of 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">LEMinIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">SMinIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">MaxIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// 0</span> <span class="p">}</span> </pre></div></div> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/9449 Trac 1.4.3 Rein Halbersma <rhalbersma@…> Mon, 02 Dec 2013 11:27:48 GMT <link>https://svn.boost.org/trac10/ticket/9449#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9449#comment:1</guid> <description> <p> Sorry, the bug report contained bad code: <code>lambda&lt;Predicate, _2, _1&gt;</code> (not sure why this even compiled since lambda only takes 2 arguments). The (now hopefully) correct fix should be to write <code>bind&lt;typename lambda&lt;Predicate&gt;::type, _2, _1&gt;</code> instead. </p> <p> Because <code>max_element</code> takes a lambda expression (metafunction class or placeholder expression), and because <code>bind</code> takes only a metafunction class, we need <code>lambda&lt;Predicate&gt;::type</code> to convert a placeholder expression <code>Predicate</code> into a metafunction class. </p> <p> Corresponding modified test program at: <a class="ext-link" href="http://coliru.stacked-crooked.com/a/f8658230325c9dd8"><span class="icon">​</span>http://coliru.stacked-crooked.com/a/f8658230325c9dd8</a> </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/begin_end.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/bind.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/distance.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/lambda.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/less_equal.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/max_element.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/min_element.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/placeholders.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/mpl/vector_c.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp"></span> <span class="k">using</span> <span class="k">namespace</span> <span class="n">boost</span><span class="o">::</span><span class="n">mpl</span><span class="p">;</span> <span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">Sequence</span><span class="p">,</span> <span class="k">class</span> <span class="nc">Predicate</span> <span class="o">=</span> <span class="n">less</span><span class="o">&lt;</span><span class="n">_1</span><span class="p">,</span> <span class="n">_2</span><span class="o">&gt;&gt;</span> <span class="k">struct</span> <span class="nl">stable_min_element</span> <span class="p">:</span> <span class="c1">// mpl::min_element uses max_element&lt;Sequence, not_&lt;Predicate&gt;&gt;</span> <span class="c1">// max_element takes a lambda expression (metafunction class or placeholder expression)</span> <span class="c1">// bind takes only a metafunction class, so we need lambda&lt;Predicate&gt;::type </span> <span class="c1">// to convert a placeholder expression Predicate into a metafunction class</span> <span class="n">max_element</span><span class="o">&lt;</span><span class="n">Sequence</span><span class="p">,</span> <span class="n">bind</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">lambda</span><span class="o">&lt;</span><span class="n">Predicate</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">_2</span><span class="p">,</span> <span class="n">_1</span><span class="o">&gt;&gt;</span> <span class="p">{};</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="k">using</span> <span class="n">V</span> <span class="o">=</span> <span class="n">vector_c</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">MinIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">min_element</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">LEMinIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">min_element</span><span class="o">&lt;</span><span class="n">V</span><span class="p">,</span> <span class="n">less_equal</span><span class="o">&lt;</span><span class="n">_1</span><span class="p">,</span> <span class="n">_2</span><span class="o">&gt;</span> <span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">SMinIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">stable_min_element</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">MaxIdx</span> <span class="o">=</span> <span class="n">distance</span><span class="o">&lt;</span><span class="n">begin</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="p">,</span> <span class="n">max_element</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">MinIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// ERROR: prints 2 instead of 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">LEMinIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">SMinIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">MaxIdx</span><span class="o">::</span><span class="n">value</span><span class="p">;</span> <span class="c1">// 0</span> <span class="p">}</span> </pre></div></div> </description> <category>Ticket</category> </item> </channel> </rss>