On Fri, 28 Feb 2020 at 17:48, Alexander Grund via Boost < boost@lists.boost.org> wrote:
There exists no negative size. This IS exactly why you should use signed int's So to represent a number that is always unsigned you should use a signed type? , so overflow is easy to detect. That is a different use case: An operation, not a representation. Nothing stops you from converting to signed, do what you want including your "check for negative"-based overflow detection and finally converting back to unsigned when passing it to an API expecting an unsigned number (number not type, although the type is unsigned too, see above) By the time you (I don't mean You of course, sorry for my bad english) wrapped almost (a little bit less than 2^64) around the std::size_t integer line Didn't you argue in the mail before that there will never be anything of size 2^32 and hence even not anything like 2^64? How could you overflow
Am 28.02.20 um 21:15 schrieb degski via Boost: that then?
Finding bugs related to this is hard, using int's you'll know right away. How? Only if you underflow. On unsigned you'll get a very large number if you go below zero, on signed you get a negative number. Both can be detected. But you talked about overflow. For unsigned you'll get a small number (that is wrong obviously but you COULD check) but for signed you get UB and hence can't even check for that.
If you get an unsigned value there is no need to check for below zero, if you get a signed value you might. It is the same there is `not_null<T>` in GSL(?).
But you would need to check if it wrapped No. If you call `obj.size()` you get an unsigned value that is a valid unsigned value. It cannot wrap when returning (conditions apply). If obj.size() returns a signed value you'll got to check for <0 before using the value unless the API somehow promises to not return negative values. Encoding this in the type is the natural thing to do. and if you start adding and subtracting these things See above. For a representation (and hence API) unsigned makes sense. For using arbitrary operations it may not. Use the types that fir your use case. Adding is save for unsigned, as you argued: The type is wide enough for all uses as a size of something. Subtraction might not but you can check first (`if(a <= size) return size - a; else throw `) (the int's should be sized to requirement of course like OP has implemented, I applaud this, now it only needs to also be possible to make that signed). You mean different widths? You'll still have to check if you can "downcast/narrowcast" it before doing so.
The whole discussion just shows that there is a problem with operations
mixing signed and unsigned types in C++ in general.
Yes, solution: signed, slightly (a power of 2, :-) ) smaller, but that is not relevant for actual 'problems' in this (our) world. See above: Use signed for operations if you want to detect underflow.
What we probably wanted was something like `size_t = not_negative<int>`, but well...
I don't understand.
Maybe after the above? You want a guarantee to have a non-negative number. "unsigned" is that but it suffers from underflow going undetected. A `not_null<T>` like "wrapper" which otherwise behaves as T but guarantees the non-negativity would make the type suitable for representing an unsigned number in a signed type suitable for operations. Obviously if you subtract something from a `not_negative<int>` it will become a plain "int". Once you pass it to an API expecting a `not_negative<int>` the precondition will be checked.