Could you please try and explain, why you think that signed is not a good type for a size (other than stating that "size cannot be negative"), what I am saying is that "valid-size cannot be negative"? You could also make the size a complex number, that would be an analogue. The imaginary-componant will have to be zero, but otherwise it would work just fine. The fact that that set is larger than the problem domain is IMO orthogonal to that.
Because if you use a signed type you have no compile-time guarantee that the value is unsigned (using "type" and "value" to differentiate those 2). Same as with a pointer: It can be NULL. If you want an interface where you want to guarantee at compile-time that the value passed over an API is never NULL, you use e.g. a reference that can never be NULL (or not_null<T>)
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 that then?
If you are manipulating (subtracting, adding differences) pointers, I thought I wrote that. The pointers might be pointers into Virtual Memory and can have any value [1, 2^56].
Not sure I understand that. Can we agree that a 64-bit unsigned type is big enough to store any size of any container and hence no overflow is possible?
> 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).
Tautology: "... an unsigned value that is a valid unsigned value", they always are, whether it's the right number is another question.
Ok: "you get an unsigned type that is a valid unsigned value". If the size was signed you get a signed type which may be an unsigned value. You'll have to check.
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.
No, it's not, unsigned types are good for bit-manipulation only, nothing else. Unsigned types don't follow the rules of mathematics, they are fundamentally flawed by nature. The fact that int's are limited in range is not flaw, but an implementation detail. The mathematically correct way of doing things (on a Turing-machine) is to use signed big-ints.
I disagree. And as mentioned you can do things like `int difference = int(obj.size()) - 1` anytime you want to do operations that are not fully defined on unsigned types (as in: may result in values outside the range) same as you can't to `int foo = sqrt(integer)` because you may get an imaginary number (if sqrt could do that, but I think you get the gist).
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 `)
You've now just precluded the use of noexcept (noexcept move f.e., super-important in modern C++) and added a branch (cannot be removed by something clever, exactly because it is unsigned, the compiler can make no assumptions and the code has to go through the math) to your code,
What is much better is to use signed int's combined with assert's.
all that because it upsets you that something that should not occur in the first place in correct code can occur iff one is writing code
The use of unsigned is false security (actually no security) and serves nothing. In the end, you still need to write correct code (so the signed int's WON'T BE negative, there where they shouldn't be), but this practice makes you're code less flexible, more verbose (the unavoidable casts add to that) and probably slower than using signed. All that because of this 'natural' way of looking at sizes. It serves as a contract on the API level: "This value is unsigned. Period." If the type was signed you'd need something else to enforce
How is that any different from `assert(a <= size); return size - a;`? that now (as one observed it got negative) is known to have a bug. Again: How is that different to using a signed type for "size"? You have exactly the same potential for bugs. You always have to make sure you stay in your valid domain, and a negative size is outside of that valid domain. Hence you got to check somewhere or use control flow to make sure this doesn't happen. So no difference in signed vs unsigned size regarding that that the value is unsigned. So yes you still need to write correct code and passing a negative value to an API expecting an unsigned value is in any case a bug.
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.
Got it now, yeah that would be great, but for now that would be run-time, no? And I guess, due to the halting problem, it can never be compile-time, unless it's a limited problem.
Surely at runtime. How else could you guarantee that your value isn't
negative after you subtract something from it? It can be compile-time if
you only add something to it and ignore overflow but you already do that
when using signed values anyway.
But all this arguing doesn't solve much: What piece of code would
actually benefit from having a signed size? And not only the part where
you request the size and use it, but also the part where you give that
size back to the object, so you'll need to ensure an unsigned value. And
yes `for(int i=0; i