Boost C++ Libraries: Ticket #13094: boost::adaptors::transform fails to preserve bidirectional behavior of range https://svn.boost.org/trac10/ticket/13094 <p> The documentation of <code>boost::adaptors::transform</code> states the returned range type is the same as the argument range type. However, when wrapping <code>std::vector</code>, the iterators returned are not bidirectional as the <code>std::vector</code> iterators are. <code>std::advance</code> with a negative integer leaves them unchanged and <code>std::prev</code> fails to compile with them. They do work as expected with <code>operator-</code>. </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;iterator&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/range/adaptor/transformed.hpp&gt;</span><span class="cp"></span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">data</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">};</span> <span class="k">auto</span> <span class="n">transformed</span> <span class="o">=</span> <span class="n">boost</span><span class="o">::</span><span class="n">adaptors</span><span class="o">::</span><span class="n">transform</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="p">[](</span><span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span><span class="p">;</span> <span class="p">});</span> <span class="k">auto</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">transformed</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="c1">// auto iter = data.begin(); // swap to compare</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;begin: &quot;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">iter</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// prints 1 both cases</span> <span class="n">std</span><span class="o">::</span><span class="n">advance</span><span class="p">(</span><span class="n">iter</span><span class="p">,</span> <span class="mi">1</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="s">&quot;begin+1: &quot;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">iter</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// prints 2 both cases</span> <span class="n">std</span><span class="o">::</span><span class="n">advance</span><span class="p">(</span><span class="n">iter</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// prints 2 for boost adaptor, 1 for std::vector</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;begin+1-1: &quot;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">iter</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</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="s">&quot;begin+1-1 (v2): &quot;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="p">(</span><span class="n">iter</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// prints 1 both cases</span> <span class="c1">// std::prev(iter); // boost fails bidirectional test, won&#39;t compile</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div></div> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/13094 Trac 1.4.3 Jake Cobb <jake.cobb@…> Sat, 24 Jun 2017 00:52:08 GMT <link>https://svn.boost.org/trac10/ticket/13094#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/13094#comment:1</guid> <description> <p> By the way I see this with both clang and Visual Studio libraries. It seems that <code>std::advance</code>, <code>std::prev</code>, etc are relying on <code>std::iterator_traits&lt;Iter&gt;::iterator_category</code> tag dispatch, which for <code>std::vector</code> yields: </p> <p> <code>std::random_access_iterator_tag</code> </p> <p> But for <code>boost::adaptors::transform</code> yields: </p> <p> <code>boost::iterators::detail::iterator_category_with_traversal&lt;std::input_iterator_tag, boost::iterators::random_access_traversal_tag&gt;</code> </p> <p> It looks like there may be code for boost to understand the standard library <code>iterator_category</code> tags in terms of traversals, but the opposite does not work; this causes behavior changes when passing to code using standard library conventions when the code is modified from passing standard types to boost adaptor types. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Michel Morin</dc:creator> <pubDate>Sat, 24 Jun 2017 02:50:36 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/13094#comment:2 https://svn.boost.org/trac10/ticket/13094#comment:2 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> Random Access Traversal Iterator (in the Boost iterator traversal concepts) is not the same thing as Random Access Iterator (in the Standard iterator categories). </p> <p> The former just specifies traversal requirements, but the latter also specifies value access requirements. (For more details, please consult to the documentation of Boost.Iterator.) For example, Random Access Iterator requires that iterator's <code>reference</code> type is a real reference. The iterator in the test case fails to satisfy this requirement. </p> <p> That being said, I think it would be useful to have <code>boost::advance</code>, which is parameterized by the Boost iterator traversal concepts. I'm going to ask about it on the developer ML. </p> Ticket