简体   繁体   中英

c++ iterate one item - UB or defined behaviour?

I've been thinking about the rules for pointer arithmetic in c++ recently, having learned that pointer arithmetic is only defined for pointers that point at objects that exist in an array.

This led me to wonder whether the following code's behaviour is strictly undefined according to the standard.

Can anyone shed any light?

#include <iostream>
#include <algorithm>
#include <iterator>
#include <utility>
#include <string>
#include <vector>

struct Thing {
    std::string val;
};

int main() {

    Thing a_thing;

    std::vector<Thing> things;

    // take address
    auto first_thing = std::addressof(a_thing);  

    // take address of "one past the end" - UB?
    auto last_thing = std::next(first_thing);    

    // copying exactly one item, but is it UB?
    std::copy(first_thing, last_thing, std::back_inserter(things));  
}

According to 5.7 [expr.add] paragraph 4 a pointer to an object behaves like a pointer the first object of a one element array:

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.

This is well-formed.

[expr.add]/4 states:

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.

After which it is stated ([expr.add]/5):

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.

As the other answers mentioned, your program is well-formed. Here's the relevant standardese from the latest working paper:

§5.7 (footnote 86) [expr.add]

An object that is not an array element is considered to belong to a single-element array for this purpose; see [expr.unary.op] . A pointer past the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n] for this purpose; see [basic.compound] .

In short, a non-array object behaves like a single-element array in the context of pointer arithmetic. Taking one-past-the-end address is therefore legal.

The program is well-formed.

You are allowed to set a pointer one past the end of an array or even just beyond the of the address of a scalar, as if it were a single element array.

That second part is important here. So long as you don't actually dereference last_thing , which std::copy will not, your code will run perfectly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM