Imagine that you have this generic pseudo-code:
template<typename Iterable>
void f(Iterable&& iterable)
{
...
}
We want to handle rvalue and lvalue references to iterable objects1, and the idea is that the function handles the container performing operations element by element.
It is plausible that we want to forward the reference specification of the container to the elements. In other words, if iterable is an rvalue reference, the function will have to move the elements from the container.
Using C++17, I would do
auto [begin, end] = [&] {
if constexpr(std::is_lvalue_reference_v<Iterable>)
return std::array{std::begin(iterable),
std::end(iterable)};
else
return std::array{
std::make_move_iterator(std::begin(iterable)),
std::make_move_iterator(std::end(iterable))};
}();
std::for_each(begin, end, [&](auto&& element)
{
...
});
Obviously, this is not the best code to maintain2, error prone and probably not so easy to optimize for the compiler.
My question is: it could be possible, for future C++ standards, to introduce the concept of forwarding range-based loops? It would be nice if this
for(auto&& el : std::move(iterable))
{
...
}
could handle el as rvalue reference. In this way, this would be possible:
template<typename Iterable>
void f(Iterable&& iterable)
{
for(auto&& el : std::forward<Iterable>(iterable))
{
/*
* el is forwarded as lvalue reference if Iterable is lvalue reference,
* as rvalue reference if Iterable is rvalue reference
*/
external_fun(std::forward<decltype(el)>(el));
}
}
I am concerned about code-breaking changes, but at the same time I am not able to think about situations in which passing a rvalue reference as argument of a range based loop is expected to work without moving objects.
As suggested, I tried to write down how I would change the 6.5.4 section of the standard. The draft can be read at this address.
Do you think that it would be possible to introduce this feature without introducing serious issues?
1Checked with C++20 concepts or static_asserts
2And it's quite worse without C++17
This won't work. Fundamentally there are two kinds of things you can iterate over: those that own the elements, and those that don't. For non-owning ranges, the value category of the range is immaterial. They don't own their elements and so you can't safely move from them. The range-based for loop must work with both kind of ranges.
There are also corner cases to consider (e.g., proxy iterators). The range-based for loop is basically syntax sugar that imposes only a very minimal set of requirements on the thing being iterated over. The benefit is that it can iterate over lots of things. The cost is that it doesn't have much room to be clever.
If you know that the iterable in fact owns its elements (so that moving is safe), then all you need is a function that forwards something according to the value category of some other thing:
namespace detail {
template<class T, class U>
using forwarded_type = std::conditional_t<std::is_lvalue_reference<T>::value,
std::remove_reference_t<U>&,
std::remove_reference_t<U>&&>;
}
template<class T, class U>
detail::forwarded_type<T,U> forward_like(U&& u) {
return std::forward<detail::forwarded_type<T,U>>(std::forward<U>(u));
}
You may add a wrapper, something like:
template <typename T> struct ForwardIterable;
template <typename T> struct ForwardIterable<T&&>
{
ForwardIterable(T&& t) : t(t) {}
auto begin() && { return std::make_move_iterator(std::begin(t)); }
auto end() && { return std::make_move_iterator(std::end(t)); }
T& t;
};
template <typename T> struct ForwardIterable<T&>
{
ForwardIterable(T& t) : t(t) {}
auto begin() { return std::begin(t); }
auto end() { return std::end(t); }
auto begin() const { return std::begin(t); }
auto end() const { return std::end(t); }
T& t;
};
template <typename T>
ForwardIterable<T&&> makeForwardIterable(T&& t)
{
return {std::forward<T>(t)};
}
And then
for(auto&& el : makeForwardIterable(std::forward(iterable)))
{
// ...
}
your suggestion will introduce breaking changes. Assume this piece of code:
vector<unique_ptr<int>> vec;
for (int i = 0; i < 10; ++i)
vec.push_back(make_unique<int>(rand()%10));
for (int i = 0; i < 2; ++i) {
for (auto &&ptr : move(vec))
cout << (ptr ? *ptr : 0) << " ";
cout << endl;
}
With current standard, it'll print two same lines
Write a simple range type. It stores two iterators and exposes begin() and end().
Write a move_range_from(Container&&) function that returns a range of move iterators.
Write move_range_from_if<bool>(Container&&) that creates a range or moves from the range conditionally.
Support lifetime extension in both.
template<typename Iterable>
void f(Iterable&& iterable) {
auto move_from = std::is_rvalue_reference<Iterable&&>{};
for(auto&& e: move_range_from_if< move_from >(iterable) ) {
}
}
does what you want.
This supports both ranges (non-owning) and containers and doesn't require a language extension. And it doesn't break existing code.
The lifetime extension feature is so you can call these functions with prvalues; without it, for(:) loops don't lifetime extend arguments to the loop target function call.
Related
Consider a function that accepts one or more parameters (e.g. file names). In order to make it versatile, it is advantageous to write it for a general iterator range:
template<class Iter>
void function(Iter first, Iter last)
{
// do something
}
Now we can invoke it in the following way, independently of how we store the arguments:
WhateverContainer container;
function(std::begin(container), std::end(container));
For example, the STL relies heavily on this paradigm.
Now, imagine we want to invoke the function with a single argument that is not stored in a container. Of course we can write:
const int value = 5;
std::vector<int> vec(1, value);
function(std::begin(vec), std::end(vec));
But this solution seems clumsy and wasteful to me.
Question: Is there a better low-overhead way of creating an iterator-range-compatible representation of a single variable?
You can use pointers, for once:
function(&value, &value + 1);
In generic code, std::addressof instead of the unary operator & is somewhat safer, depending on your level of paranoia.
You can of course wrap this in an overload for easier use:
template <class T>
decltype(auto) function (T &&e) {
auto p = std::addressof(e);
return function(p, p + 1);
}
You can treat it like an array of one element per [expr.unary.op]/3:
function(&value, &value + 1);
For purposes of pointer arithmetic ([expr.add]) and comparison ([expr.rel], [expr.eq]), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T.
You can also overload your function template function for a single-element range:
template<typename Iter>
void function(Iter first) {
return function(first, std::next(first)); // calls your original function
}
This way, your original function function remains compatible with iterator ranges. Note, however, that using this overload with an empty range will result in undefined behavior.
For a single element, value, you can use the overload above:
function(&value); // calls overload
Since operator & may be overloaded, consider also using std::addressof instead of &, as already mentioned in this answer.
For a range consisting of a single element, you can use the overload above as well, which only needs a single iterator instead of an iterator pair:
const int value = 5;
std::vector<int> vec(1, value); // single-element collection
function(std::begin(vec)); // <-- calls overload
I think I'd do this in two steps:
Define a overload of the template function that takes a container, written in terms of the iterator version.
Define a proxy class which treats an object reference as an array of size 1.
c++17 example:
#include <iterator>
#include <type_traits>
#include <vector>
#include <iostream>
// proxy object
template<class T>
struct object_as_container
{
using value_type = T;
using iterator = T*;
using const_iterator = std::add_const_t<T>;
object_as_container(value_type& val) : object_(val) {}
const_iterator begin() const { return std::addressof(object_); }
iterator begin() { return std::addressof(object_); }
const_iterator end() const { return std::next(begin()); }
iterator end() { return std::next(begin()); }
private:
value_type& object_;
};
// our function in terms of iterators
template<class Iter> void func(Iter first, Iter last)
{
while(first != last)
{
std::cout << *first++;
}
}
// our function in terms of containers
template<class Container> void func(Container&& cont)
{
func(cont.begin(), cont.end());
}
int main()
{
const int value = 5;
func(object_as_container(value));
func(std::vector { 1,2,3,4,5 });
}
I have a pointer "a", it is of type A*. I now that there is n objects of type A at that address and I want to iterate over them.
I would like to cast it to A[n], so that I can use the c++11 range-for and write for (auto temp : a){...}.
Of course, I can use a classic for(int i=0; i<n; i++) {temp=a[i]; ...} but the range-for is cleaner.
In reasonable code, I'd shy away from it. But C++ allows you to commit acts of sheer devilry. In that vein, I offer a solution:
At the expense of some considerable obfuscation, you can write some preparatory templates:
namespace std
{
template <typename T> T* begin(std::pair<T*, T*> const& a)
{
return a.first;
}
template <typename T> T* end(std::pair<T*, T*> const& a)
{
return a.second;
}
}
Then you can write something like
for (auto&& i : std::make_pair(a, a + n)){
}
The template stuff brings in suitable definitions of begin and end which is required by the range for loop.
Consider that I have a std::vector.
std::vector<int> blah;
blah.push_back(1);
blah.push_back(2);
I now want to pass the vector somewhere and disallow modifying the contents of the objects its contains while still allowing to modify the container when able:
// Acceptable use:
void call_something() {
std::vector<int> blah;
blah.push_back(1);
blah.push_back(2);
// Currently, compiler error because of mismatching types
something(blah);
}
void something(std::vector<const int>& blah)
{
// Auto translates to 'const int'
for ( auto& i : blah ) {
// User cannot modify i.
std::cout << i << std::endl;
}
blah.push_back(blah.size()); // This should be acceptable
blah.emplace_back(); // This should be acceptable
return;
}
// Unacceptable use:
void something_else(const std::vector<int>& blah)
{
// Because of const vector, auto translates to 'const int'
for ( auto& i : blah ) {
std::cout << i std::endl;
}
blah.push_back(blah.size()); // This will present an unacceptable compiler error.
blah.emplace_back(); // This will present an unacceptable compiler error.
return;
}
Is there an easy way to do this?
To enable the operations you wish to allow while preventing the others, you need to take a fine-grained approach to your function's interface. For example, if your calling code were to pass const iterators (begin and end) as well as a back inserter (or custom back emplacer functor), then exactly the subset of operations you showed would be possible.
template <class Iter, class F>
void something(Iter begin, Iter end, F&& append)
{
using value_type = typename std::iterator_traits<Iter>::value_type;
std::copy(begin, end, std::ostream_iterator<value_type>(std::cout, "\n"));
append(std::distance(begin, end));
append();
return;
}
That said I don't find your examples particularly compelling. Do you have a real scenario in which you must maintain mutable elements, pass a mutable container to a function, yet treat the passed elements as immutable?
There is no easy way to do this. One way would be to wrap a vector in a type that exposes only the functionality that you want to allow. For instance
template<typename T, typename A = std::allocator<T>>
struct vector_wrap
{
using iterator = typename std::vector<T, A>::const_iterator;
using const_iterator = typename std::vector<T, A>::const_iterator;
using size_type = typename std::vector<T, A>::size_type;
vector_wrap(std::vector<T, A>& vec)
: vec_(&vec)
{}
void push_back(T const& value) { vec_->push_back(value); }
void push_back(T&& value) { vec_->push_back(std::move(value)); }
size_type size() { return vec_->size(); }
iterator begin() const { return vec_->cbegin(); }
iterator end() const { return vec_->cend(); }
private:
std::vector<T, A> *vec_;
};
Since the above implementation only stores a pointer to the vector it wraps, you'll have to ensure that the lifetime of the vector is longer than that of vector_wrap.
You'll have to modify something and something_else so that they take a vector_wrap<int> as argument. Since vector_wrap::begin and vector_wrap::end return const_iterators, you'll not be allowed to modify existing elements within the for statement.
Live demo
I'm currently using a third-party library which contains a class that only provides indexed lookup, i.e. operator[].
I'd like to do a range-based for loop on this class's contents. However, having never written an iterator or iterator adapter, I'm quite lost. It seems that writing iterators is not a straightforward task.
My desired usage is:
for(auto element : container)
{
...
}
Instead of having to write:
for(int i = 0; i < container.size(); ++i)
{
auto element = container[i];
...
}
How can this be achieved? Does Boost provide this functionality?
Writing iterators is actually a rather straightforward task, but it gets extremely tedious. Since your container supports indexing by an integer, I assume its iterators would fall into the random access iterator category (if they existed). That needs a lot of boilerplate!
However, to support the range-based for loop, all you'll need is a forward iterator. We'll write a simple wrapper for the container that implements the forward iterator requirements, and then write two functions Iterator begin(Container&) and Iterator end(Container&) that enable the container to be used in the range-based for loop.
This Iterator will contain a reference to the container, the size of the container, and the current index within that container:
template<template<typename> class C, typename T>
class indexer : public std::iterator<std::forward_iterator, T>
{
public:
indexer(C<T>& c, std::size_t i, std::size_t end)
: c_(std::addressof(c)), i_(i), end_(end) {}
T& operator*() const {
return c_[i_];
}
indexer<C, T>& operator++() {
++i_;
return *this;
}
indexer<C, T> operator++(int) {
auto&& tmp = *this;
operator++();
return tmp;
}
bool operator==(indexer<C, T> const& other) const {
return i_ == other.i_;
}
bool operator!=(indexer<C, T> const& other) const {
return !(*this == other);
}
private:
C<T>* c_;
std::size_t i_, end_;
};
Inheriting from std::iterator conveniently declares the appropriate typedefs for use with std::iterator_traits.
Then, you would define begin and end as follows:
template<typename T>
indexer<Container, T> begin(Container<T>& c) {
return indexer<Container, T>(c, 0, c.size());
}
template<typename T>
indexer<Container, T> end(Container<T>& c) {
auto size = c.size();
return indexer<Container, T>(c, size, size);
}
Switch out Container for whatever the type of container is in your example, and with that, your desired usage works!
The requirements and behavior of all the various kinds of iterators can be found in the tables of section 24.2.2 of the standard, which are mirrored at cppreference.com here.
A random-access iterator implementation of the above, along with a demo of usage with a simple vector_view class can be found live on Coliru or ideone.com.
You can do the following:
1) define your own iterator It that contains, inside, a ref ref to your container container and an index i. When the iterator is dereferenced, it returns ref[i] by reference. You can write it yourself or you can use boost for help, it has an iterator library to easily define your own iterators. Constructor should accept a container& and a size_t. You can make also the const_iterator if this concept applies.
2) When operator++ is invoked on one iterator, it simply does ++i on the internal member. operator== and operator!= should simply compare i. You can assert, for security, that both iterators are coherent, that means their refs point to the same object.
3) add begin and end to your container class or, if this is not possible, define a global begin and end that accept your container& c. begin should simply return It(c, 0). end should return It(c, c.size()).
There could be a problem copying the iterators as they contain a reference and some other minor details, but I hope the overall idea is clear and correct.
I wrote a short utility function an object to "wrap" an iterable container, so that I could walk it backwards using a range based for.
template <typename Iterable>
struct ReverseWrapper {
private:
Iterable& m_iterable;
public:
ReverseWrapper(Iterable& iterable) : m_iterable(iterable) {}
auto begin() const ->decltype(m_iterable.rbegin()) {
return m_iterable.rbegin();
}
auto end() const ->decltype(m_iterable.rend()) {
return m_iterable.rend();
}
};
template <typename Iterable>
ReverseWrapper<Iterable> reverseIterate(Iterable& list) {
return ReverseWrapper<Iterable>(list);
}
This works for C++ iterable objects, but not for static arrays. What is required for an object to support iteration using a range based for? What would be the best way to approach this problem?
The actual rule to choose begin and end functions for iterables is the following: use the class begin and end function if it has some. Use overloads of the global functions std::begin and std::end if some are provided.
Static arrays not being class/struct, they don't/can't have member functions. The functions called by the foreach loop are the global functions std::begin and std::end, taking an array as parameter. Assuming std::rbegin and std::rend existed, you would have to construct your wrapper the following way:
template <typename Iterable>
struct ReverseWrapper {
private:
Iterable& m_iterable;
public:
ReverseWrapper(Iterable&& iterable) : m_iterable(iterable) {}
auto begin() const -> decltype(rbegin(m_iterable)) {
return rbegin(m_iterable);
}
auto end() const -> decltype(rend(m_iterable)) {
return rend(m_iterable);
}
};
template<typename Iterable>
auto reverseIterate(Iterable&& list)
-> ReverseWrapper<Iterable>
{
return ReverseWrapper<Iterable>(std::forward<Iterable>(list));
}
Even though std::rbegin and std::rend exist in the c++14 standard, they are not available in the c++11 one. So, to get the above code to work with c++11, you would have to implement these functions by hand:
template<typename T, std::size_t N>
auto rbegin(T (&array)[N])
-> std::reverse_iterator<T*>
{
return std::reverse_iterator<T*>(std::end(array));
}
template<typename T, std::size_t N>
auto rend(T (&array)[N])
-> std::reverse_iterator<T*>
{
return std::reverse_iterator<T*>(std::begin(array));
}
In your code, the Iterable template parameter needs to have begin and end member functions. Normal C++ arrays do not have those functions. Instead you have to use std::begin and std::end, which are part of the C++11 standard.
However, there doesn't seem to be any std::rbegin or std::rend functions, which means you have to implement those yourself, possibly also implement the actual iterator class.