|
|
|
|
|
by helmut_hed
4041 days ago
|
|
I'm curious about that last bit. I occasionally hear this argument that size_t being unsigned is a bad thing. Presumably the standard library designers would claim that a size cannot be negative, so this is appropriate. Why, in your view, should that be a signed quantity? |
|
Now, you often want to do arithmetic with numbers, right? You could think that, given two containers with c1.size() < c2.size(), that c1.size()-1 < c2.size()-1, right? Wrong.
Because of default promotion rules and size() returning an unsigned number, (signed) 1 is converted to an unsigned number and the whole expression is evaluated in modulo arithmetic.
Now, if c1 is empty (c1.size() == 0), then subtracting 1 from 0 in unsigned arithmetic wraps around (this is how unsigned arithmetic is defined) to a huge number (4 billion on 32 bit machines). The simple comparison above will fail in THE singular case of having an empty container on left hand side.
You also lose any possibility of detecting errors. (E.g., if size() were signed and could return a negative number, you could effectively assert on that. If assert triggered, then something really bad happened -- maybe two concurrent threads modifying the data structure w/o proper synchronization.)
I personally think it makes reasoning about a program harder; the above comparison was just the most trivial example. unsigned<>signed comparisons also trigger a bunch of warnings, because I use signed numbers for everything else, so the code ends up having a bunch of unnecessary casts. (I do not want to disable the warning because genuine mishaps of mixing signed/unsigned do happen.)
I believe the consensus is (also Stroustrup's recommendation) is that unsigned should be used as "give me modulo arithmetic" instead of "this number cannot be negative".
So, IMHO, unsigned size() was a mistake and is a big PITA when you try to develop robust software and creates less maintainable code (e.g. in the above simple example, you'd have to special-case the c1.empty() case with if). Casting size() to signed type immediately if I'm going to do anything else than iterate over the container is the least evil for me.