Thank you for the link.
I must say I disagree with the reasons presented there. I won't touch
here the discussion of whether uncaught_exceptions() is the appropriate
interface for scope guard/transaction implementation. I'll just say that
this interface is enough for my needs currently.
First of all, and most importantly, I believe interface clarity is what
matters most, especially in the case of standard interfaces. I have said
in the OP that using a signed type implies that the function can return
negative values but the meaning of these values is not defined nor can
be guessed. The current interface is less clear than it can (and
should), if not confusing.
Secondly, since negative values are possible and don't make sense, good
code will have to check the returned value before using it. The error
handling can be case-specific, most likely an exception or assert/abort.
In any case, this effectively nullifies any possible gain from
optimizations the compiler might apply. The linked discussion didn't
present what such optimizations might be, but I don't believe they would
have any measurable effect in 99% cases. If the effect is measurable and
it matters, it is better to resort to assembler anyway (or redesign your
code). Note: I'd like to stress that I'm talking about the effect
compared to a similar code operating on unsigned integers.
Thirdly, sloppy code will omit the check for negative values, and I
believe this will be the most common case. It might not matter with the
simplest uses of uncaught_exceptions() (i.e. just compare the two ints
for inequality in destructor) but it is possible that more complex uses
appear where negative values are harmful. It is easy to forget the
check, and as such the interface is error prone.
Lastly, regarding the guideline to use signed integers for regular math
and unsigned for bit operations. I didn't find any technical reason for
this distinction. Overflows happen with both signed and unsigned
integers, and in general I'd say there are just as many ways to screw up
with signed integers as there are with unsigned. New users will have to
learn both signed and unsigned integers anyway, I don't see a problem here.
A little lyrical digression. I have always thought of signed integers as
of unsigned integers having an additional property of being negative. I
know there are differences in behavior in different contexts, but to me
the sign is an optional property which should be used only when
required. And it so happens that most of the math I do in code is with
positive integers. It is only natural not to use signed integers for
that because doing so would be confusing and misguiding for the readers
of the code. When you use a signed integer, you have to always be aware
of its sign, which is another thing to track in mind, and this is
unnecessary work that can be avoided with unsigned integers. This is
akin to floating point numbers with which you also have to be aware of
rounding and computation error. Having signed integers in the standard
library where unsigned should have been will surely complicate my work
as I will have to add checks for negative values and cast to unsigned.