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.
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>{});
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With