First, the specification is that it will return a value less
than, equal to or greater than 0
, not necessarily -1
or 1
.
Secondly, return values are rvalues, subject to integral
promotion, so there's no point in returning anything smaller.
In C++ (as in C), every expression is either an rvalue or an
lvalue. Historically, the terms refer to the fact that lvalues
appear on the left of an assignment, where as rvalues can only
appear on the right. Today, a simple approximation for
non-class types is that an lvalue has an address in memory, an
rvalue doesn't. Thus, you cannot take the address of an rvalue,
and cv-qualifiers (which condition "access") don't apply. In
C++ terms, an rvalue which doesn't have class type is a pure
value, not an object. The return value of a function is an
rvalue, unless it has reference type. (Non-class types which
fit in a register will almost always be returned in a register,
for example, rather than in memory.)
For class types, the issues are a bit more complex, due to the
fact that you can call member functions on an rvalue. This
means that rvalues must in fact have addresses, for the this
pointer, and can be cv-qualified, since the cv-qualification
plays a role in overload resolution. Finally, C++11 introduces
several new distinctions, in order to support rvalue references;
these, too, are mainly applicable to class types.
Integral promotion refers to the fact that when integral types
smaller than an int
are used as rvalues in an expression, in
most contexts, they will be promoted to int
. So even if
I have a variable declared short a, b;
, in the expression a
+ b
, both a
and b
are promoted to int
before the addition
occurs. Similarly, if I write a < 0
, the comparison is done
on the value of a
, converted to an int
. In practice, there
are very few cases where this makes a difference, at least on
2's complements machines where integer arithmetic wraps (i.e.
all but a very few exotics, today—I think the Unisys
mainframes are the only exceptions left). Still, even on the
more common machines:
short a = 1;
std::cout << sizeof( a ) << std::endl;
std::cout << sizeof( a + 0 ) << std::endl;
should give different results: the first is the equivalent of
sizeof( short )
, the second sizeof( int )
(because of
integral promotion).
These two issues are formally orthogonal; rvalues and lvalues
have nothing to do with integral promotion. Except...
integral promotion only applies to rvalues, and most (but not
all) of the cases where you would use an rvalue will result in
integral promotion. For this reason, there is really no reason
to return a numeric value in something smaller than int
.
There is even a very good reason not to return it as
a character type. Overloaded operators, like <<
, often behave
differently for character types, so you only want to return
characters as character types. (You might compare the
difference:
char f() { return 'a'; }
std::cout << f() << std::endl;
std::cout << f() + 0 << std::endl;
The difference is that in the second case, the addition has
caused integral promotion to occur, which results in a different
overload of <<
to be chosen.
string::compare()
you link to clearly states the return value is <0, 0, and >0 -not- -1, 0 and 1. – Captain Obvliousshort
orchar
instead ofint
? Most architectures are going to store the return value of a function in a register, and anint
will fit in a register just as well as ashort
orchar
. And usingchar
for numeric types is always a bad idea, especially when you need to guarantee signed values are handled correctly. – Cody Gray♦char
would be a bad idea, since code checking for the return value if it's less than zero will fail on platforms wherechar
is unsigned. – milleniumbug