0

I am implementing an Array class with constexpr methods. In my design, I allow an iterator to exceed the end() because instead of doing: while ( it != arr.end() ), I do: while ( arr.finished(it) ) by comparing the two raw pointers internally. It works with non constexpr array instances, but not with constexpr ones because of the following problem:

#include <array>

int main()
{
    static constexpr std::array<int, 3> arr{};

    constexpr const int* ptr = &arr[0];

    constexpr const int* ptr2 = (const int*)ptr + 4;// error

    return 0;
}

I got the message:

array subscript value 4 is outside the bounds of array type _Type {aka const int [3]}|

https://godbolt.org/z/MjbqEv

With Windows 10 and gcc10.1.0 located at C:\cygwin64\usr\local\bin

I don't even dereference the pointer so I kinda feel angry by the "careness" of the compiler.

My question is:

What can I do to make this code compile and keep my design choice of having out of bound iterators ? (not just past last end which is already allowed by the compiler). Can I disable the out of bound error with gcc ?

Marek R
  • 32,568
  • 6
  • 55
  • 140
Cevik
  • 313
  • 1
  • 4
  • 17
  • 5
    When you are using `constexpr`, compiler is obliged to check for UB. It cannot ignore it. Since pointer arithmetic outside of array bounds is UB, compiler must report error. – Yksisarvinen Sep 23 '20 at 12:22
  • 3
    Going beyond `end()` is UB by the standard. – Evg Sep 23 '20 at 12:24
  • `constexpr` are able to detect UB and thread them as an error. So your code has UB which should be fixed. – Marek R Sep 23 '20 at 12:25
  • @Evg Thx for the comments, Is there a reason for that ? – Cevik Sep 23 '20 at 12:25
  • reason for what? You are never allowed to increment a pointer any more than 1 beyond the last element of an array (or 1 past a single object) – 463035818_is_not_an_ai Sep 23 '20 at 12:27
  • maybe you should read about UB philosophy https://stackoverflow.com/a/2772091/1387438 when constexpr is evaluated speed is not a priority so UB can be detected and reported as an error. – Marek R Sep 23 '20 at 12:27
  • @idclev 463035818 in single dimension, it's obvious that we stop incrementing an iterator once end() is reached. But in multiple dimension, we can increment it following a direction with an offset, for example looking at y0, y1, y2 in a 3*3 matrix. What is happening when incrementing the iterator by 3 one more time is a beyond end operation very natural regarding the implementation. And I don't see any UB outside the encapsulating iterator class. – Cevik Sep 23 '20 at 12:34
  • 1
    @Cevik Take a look at [this answer](https://stackoverflow.com/a/3839757): *Some architectures have dedicated registers for holding pointers. Putting the value of an unmapped address into such a register is allowed to crash.* – Evg Sep 23 '20 at 12:42
  • @Evg Can I store the address as a number or as a byte array and reinterpret cast it IF I'm gonna dereference it ? That would be the logic to me – Cevik Sep 23 '20 at 12:51
  • In C++ it is UB to access multiple dimension array as single dimension array. Read about type aliasing. – sklott Sep 23 '20 at 12:56
  • @ sklott that's not my case since i hold a std::array – Cevik Sep 23 '20 at 12:58

2 Answers2

1

You cannot perform pointer arithmetic beyond the bounds of an array in C++. The behaviour of such operation is undefined. And a program that has undefined behaviour in a constantly evaluated expression is ill-formed and therefore a compiler is allowed to refuse compiling it (and is required to diagnose it).

You cannot make this work with pointers, but you could possibly implement a custom iterator class to be used with your custom Array class instead.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • thx for the answer. I do implement such an iterator class but i cannot imagine what performances would be if I store a pointer and an offset instead of just a pointer ! – Cevik Sep 23 '20 at 12:36
  • you mean that i can store a reference to the array in the compile time version of my iterator since it's compile time ? I think it's a good idea actually ! thank you. But i would also need to disambiguate which implementation to use following the context. hum, i think there is something about is_constant_call or something in the standard... – Cevik Sep 23 '20 at 12:42
  • @Cevik Never mind, you do actually need the pointer for the iterator. Something that contains only the offset could be used with member functions of the array, which is what I had in mind for some reason. It could not be used as a general iterator. – eerorika Sep 23 '20 at 12:44
1

I don't even dereference the pointer so I kinda feel angry by the "careness" of the compiler.

What you observe is not an overly carefull compiler. Incrementing a pointer more than one past the last element of an array is not allowed. You need not dereference the pointer to be in trouble.

You are only allowed to increment the pointer up to one past the last element (and you may not dereference that one-past-the-last pointer).

In a non-const expression context incrementing ptr + 4 would be undefined behavior. During compile time however, there is no undefined behavior and the compiler diagnoses the problem.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185