1

It seems std::byte has become the way (in C++17) to work with buffers holding object representations, but it's unclear whether this intent still allows for performing pointer arithmetic.

The question in the title is intentionally phrased as should because I'm looking for recommendation. For example, void* can be used for pointer arithmetic as gcc extensions but are not standard (at least this is true for C), hence a possibility but not a recommendation.

I know the motivation for std::byte is to detach the character and the numeric aspects from the concept of byte. But at the same time, does pointer arithmetic stay?

EDIT: adjusted to clarify that I'm looking to do "pointer arithmetic" using std::byte* not the numerical value of pointers stores in std::bytes

haelix
  • 4,245
  • 4
  • 34
  • 56

1 Answers1

6

Yes, std::byte* can be used for pointer arithmetic.

And you can even do things like

struct foo{int x,y};
foo f;
int* ptr_to_y = reinterpret_cast<int*>(reinterpret_cast<std::byte*>(&f)+offsetof(foo,y));

You do have to be careful that your locations are reachable through your operations. Just because pointers-as-integers gets the right result doesn't mean that the C++ code is doing defined behavior. There are a number of quirks in C++ around permitting the optimizer to "know" that a certain value cannot be modified.

struct loc {
  int x,y;
};

void f( int* );

loc work( loc l ) {
  l.x=3;
  f(&l.y);
  return l;
}

in the above case, someone who used the &l.y pointer to do pointer arithmetic (within f) and modify l.x, regardless of if they went to std::byte* or not, would be doing undefined behavior. The compiler is allowed to assume the returned l will have an .x value of 3.

These are not new pitfalls introduced by std::byte*.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    That's got me to thinking - is the cast to/from (and arithmetic using) `byte*` permitted as a special case in the same way that it is with `char*` and `void*`? I guess that's the question after all – Lightness Races in Orbit Oct 11 '18 at 13:56
  • 2
    @LightnessRacesinOrbit Yes, `byte*` gets the same special treatement as `char*`, except it doesn't have the double-meaning of "pointer to null terminated string" and the like (so `std::ostream& << std::byte*` doesn't iterate through memory looking for a null byte, as was the fashion at the time). And you cannot do non-bit math on `byte`s, like `+2`, without first converting it to an integral type. I agree the question needs work, but I strongly believe this is what they are asking. – Yakk - Adam Nevraumont Oct 11 '18 at 13:59
  • Thanks for answering, yeah that would some of the things I'd be doing (except in your example I'd use `memcpy` to keep with strict aliasing). – haelix Oct 11 '18 at 14:01
  • @Yakk-AdamNevraumont I made a very brief attempt to find the relevant wording but don't remember where it is – Lightness Races in Orbit Oct 11 '18 at 14:22
  • @LightnessRacesinOrbit it's at [reinterpret_cast > Type Aliasing](https://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing). – haelix Oct 11 '18 at 14:31
  • @haelix I'm talking about the standard - but tbh its inclusion on that page is enough satisfaction for me! – Lightness Races in Orbit Oct 11 '18 at 14:38
  • I'm not buying the final passage though. If aliasing worked that way we'd never be able to use pointers to _anything_ with any certainty. However, if `f()` were to take something other than a `int*`, `char*`, `unsigned char*` or (it turns out) `std::byte*`, then that would indeed be a problem. And I believe that's the point in this question. – Lightness Races in Orbit Oct 11 '18 at 14:39
  • But compilers use a weaker aliasing rule -> they assume the callee may access any part of the complete object. – Oliv Oct 11 '18 at 14:50