6

I'd like to pass a single lvalue to a function which expects a pair of iterators, and for it to act as if I'd passed a pair of iterators to a range containing just this value.

My approach is as follows:

#include <iostream>
#include <vector>

template<typename Iter>
void iterate_over(Iter begin, Iter end){
    for(auto i = begin; i != end; ++i){
        std::cout << *i << std::endl;
    }
}

int main(){
    std::vector<int> a{1,2,3,4};
    iterate_over(a.cbegin(), a.cend());

    int b = 5;
    iterate_over(&b, std::next(&b));
}

This appears to work correctly in g++5.2, but I'm wondering if this is actually defined behaviour and if there are any potential issues?

  • Iterator pairs describe **ranges**. Containers are one way of creating ranges, but they are not the only way. There is no requirement that iterators point at elements of a container. – Pete Becker Jul 18 '16 at 16:19
  • Thanks, edited the question for clarity. – cloakedlearning Jul 18 '16 at 16:21
  • There are a multitude of related/duplicate questions/answers to this, including: https://stackoverflow.com/questions/14505851/is-the-one-past-the-end-pointer-of-a-non-array-type-a-valid-concept-in-c , https://stackoverflow.com/questions/2405555/string-s-s1-legal-ub , https://stackoverflow.com/questions/21411102/treating-a-single-object-like-an-array-with-one-element-taking-one-past-end-poi , etc. – WhozCraig Jul 18 '16 at 16:25
  • @WhozCraig Why not pick one to close as a dupe of and I'll delete my answer. – NathanOliver Jul 18 '16 at 16:26
  • @WhozCraig, apologies, I guess the language I used in my search/question was sufficiently different to not find them. – cloakedlearning Jul 18 '16 at 16:27
  • @NathanOliver of the ones I linked, the one that fits the best is probably the last one, but your answer is actually better, in that it covers both the sequence-of-one and the expectations of array's one-past. I'd keep it as is. – WhozCraig Jul 18 '16 at 16:28
  • @WhozCraig OK. Thanks. – NathanOliver Jul 18 '16 at 16:29

1 Answers1

11

Yes this is defined behavior. First we have from [expr.add]/4

For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

So a single object is treated as a array of length 1. Then we have [expr.add]/5

[...]Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

Emphasis mine

So since the first array element is also the last array element, and adding 1 to the last array element gives you the one past the object, it is legal.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402