Opened 5 years ago

Closed 5 years ago

#13094 closed Bugs (invalid)

boost::adaptors::transform fails to preserve bidirectional behavior of range

Reported by: Jake Cobb <jake.cobb@…> Owned by: Neil Groves
Milestone: To Be Determined Component: range
Version: Boost 1.64.0 Severity: Problem
Keywords: range adaptors transform Cc:

Description

The documentation of boost::adaptors::transform states the returned range type is the same as the argument range type. However, when wrapping std::vector, the iterators returned are not bidirectional as the std::vector iterators are. std::advance with a negative integer leaves them unchanged and std::prev fails to compile with them. They do work as expected with operator-.

#include <iostream>
#include <vector>
#include <iterator>
#include <boost/range/adaptor/transformed.hpp>

int main() {
    std::vector<int> data{1,2,3};
    auto transformed = boost::adaptors::transform(data, [](int x) { return x; });
    auto iter = transformed.begin();
    // auto iter = data.begin(); // swap to compare
    std::cout << "begin: " << *iter << std::endl; // prints 1 both cases
    std::advance(iter, 1);
    std::cout << "begin+1: " << *iter << std::endl; // prints 2 both cases
    std::advance(iter, -1);
    // prints 2 for boost adaptor, 1 for std::vector
    std::cout << "begin+1-1: " << *iter << std::endl;
    std::cout << "begin+1-1 (v2): " << *(iter - 1) << std::endl; // prints 1 both cases
    // std::prev(iter); // boost fails bidirectional test, won't compile
    return 0;
}

Change History (2)

comment:1 by Jake Cobb <jake.cobb@…>, 5 years ago

By the way I see this with both clang and Visual Studio libraries. It seems that std::advance, std::prev, etc are relying on std::iterator_traits<Iter>::iterator_category tag dispatch, which for std::vector yields:

std::random_access_iterator_tag

But for boost::adaptors::transform yields:

boost::iterators::detail::iterator_category_with_traversal<std::input_iterator_tag, boost::iterators::random_access_traversal_tag>

It looks like there may be code for boost to understand the standard library iterator_category 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.

comment:2 by Michel Morin, 5 years ago

Resolution: invalid
Status: newclosed

Random Access Traversal Iterator (in the Boost iterator traversal concepts) is not the same thing as Random Access Iterator (in the Standard iterator categories).

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 reference type is a real reference. The iterator in the test case fails to satisfy this requirement.

That being said, I think it would be useful to have boost::advance, which is parameterized by the Boost iterator traversal concepts. I'm going to ask about it on the developer ML.

Note: See TracTickets for help on using tickets.