How can I make a raw pointer behave like a range, for a for-range loop syntax.
double five = 5; double* dptr = &five; for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null Motivation:
It is now vox populi that an boost::optional (future std::optional) value can be viewed as a range and therefore used in a for range loop http://faithandbrave.hateblo.jp/entry/2015/01/29/173613.
When I rewrote my own simplified version of it:
namespace boost { template <class Optional> decltype(auto) begin(Optional& opt) noexcept{ return opt?&*opt:nullptr; } template <class Optional> decltype(auto) end(Optional& opt) noexcept{ return opt?std::next(&*opt):nullptr; } } Used as
boost::optional<int> opt = 3; for (int& x : opt) std::cout << x << std::endl; While looking that code I imagined that it could be generalized to raw (nullable) pointers as well.
double five = 5; double* dptr = &five; for(int& d : dptr) std::cout << d << std::endl; instead of the usual if(dptr) std::cout << *dptr << std::endl;. Which is fine but I wanted to achieve the other syntax above.
Attempts
First I tried to make the above Optional version of begin and end work for pointers but I couldn't. So I decided to be explicit in the types and remove all templates:
namespace std{ // excuse me, this for experimenting only, the namespace can be removed but the effect is the same. double* begin(double* opt){ return opt?&*opt:nullptr; } double* end(double* opt){ return opt?std::next(&*opt):nullptr; } } Almost there, it works for
for(double* ptr = std::begin(dptr); ptr != std::end(dptr); ++ptr) std::cout << *ptr << std::endl; But it doesn't work for the supposedly equivalent for-range loop:
for(double& d : dptr) std::cout << d << std::endl; Two compilers tell me: error: invalid range expression of type 'double *'; no viable 'begin' function available
What is going on? Is there a compiler magic that forbids the ranged-loop to to work for pointers. Am I making a wrong assumption about the ranged-loop syntax?
Ironically, in the standard there is an overload for std::begin(T(&arr)[N]) and this is very close to it.
Note and a second though
Yes, the idea is silly because, even if possible this would be very confusing:
double* ptr = new double[10]; for(double& d : ptr){...} would iterate over the first element only. A more clear and also realistic workaround would be to do something like workaround proposed by @Yakk:
for(double& d : boost::make_optional_ref(ptr)){...} In this way it is clear that we are iterating over one element only and that that element is optional.
Ok, ok, I will go back to if(ptr) ... use *ptr.
beginandendcan never be found by ADL, so the range basedforis not going to work. And I don't understand how one can think of anoptionalas a range?! You should be able to get your example to work by constructing aboost::iterator_rangewith those pointers, but forming anenditerator to a scalar object the way you are is most likely undefined behavior.namespace stdis definitely UB, though, and abusing the range-basedforfor this is rather silly.std::begin(T(&arr)[N])isn't used for range-based for loops. The core language handles arrays directly: "if_RangeTis an array type, begin-expr and end-expr are__rangeand__range + __bound, respectively, where__boundis the array bound."