Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a forward_iterator template class make sense?

The C++ standard library contains the convenient template class std::move_iterator. Given the close relationship between std::move and std::forward, why is there no equivalent std::forward_iterator? Example usage:

template <typename C>
auto foo(C&& values)
{
    remove_reference_t<C> result {};
    result.reserve(values.size());
    std::transform(std::make_forward_iterator<C>(std::begin(values)),
                   std::make_forward_iterator<C>(std::end(values))), 
                   std::back_inserter(result), Bar());
    return result;
}

The idea being I can now use foo like:

std::vector<ComplexType> values {/* lots of values */};

auto copied_fooed = foo(values); 
// or
auto moved_fooed  = foo(std::move(values));

Without having to write two foos.

like image 545
Daniel Avatar asked Mar 22 '26 08:03

Daniel


2 Answers

I can't answer why such a thing doesn't exist. But it's certainly implementable. Basically, your C type is either an lvalue reference (in which case just pass through the argument) or it's not a reference (in which case, use std::make_move_iterator).

First, let's start with a great addition to any toolkit, almost static if (with minor changes):

namespace detail {
    enum class enabler {};
}

template <bool B>
using EnableIf = std::enable_if_t<B, detail::enabler>;

And now we just use SFINAE on the two overloads:

template <typename C,
          typename Iterator,
          EnableIf<std::is_lvalue_reference<C>::value>...>
Iterator make_forward_iterator(Iterator i)
{
    return i;
}

template <typename C,
          typename Iterator,
          EnableIf<!std::is_lvalue_reference<C>::value>...>
auto make_forward_iterator(Iterator i)
{
    return std::make_move_iterator(i);
}

Alternatively, and this is probably simpler, could just tag-dispatch:

namespace detail {    
    template <typename Iterator>
    Iterator make_forward_iterator(Iterator i, std::true_type )
    {
        return i;
    }

    template <typename Iterator>
    auto make_forward_iterator(Iterator i, std::false_type )
    {
        return std::make_move_iterator(i);
    }
}

template <typename C, typename Iterator>
auto make_forward_iterator(Iterator i) {
    return detail::make_forward_iterator(i, std::is_lvalue_reference<C>{});
}
like image 152
Barry Avatar answered Mar 23 '26 22:03

Barry


It is a bit more complex than that.

With an owning container, the l/r valueness of the container imply move iterators may be of use.

But for views, this does not work: moving from an rvalue view is destructive of unowned state.

Practically, this means the decision to move or not based on l/r valueness is matter for both the context and value to mutually decide, which makes it tricky.

Range-v3 and concepts may make this easier to reason about.

like image 44
Yakk - Adam Nevraumont Avatar answered Mar 23 '26 20:03

Yakk - Adam Nevraumont



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!