Consider the following programs:
// http://ideone.com/4I0dT
#include <limits>
#include <iostream>
int main()
{
int max = std::numeric_limits<int>::max();
unsigned int one = 1;
unsigned int result = max + one;
std::cout << result;
}
and
// http://ideone.com/UBuFZ
#include <limits>
#include <iostream>
int main()
{
unsigned int us = 42;
int neg = -43;
int result = us + neg;
std::cout << result;
}
How does the + operator "know" which is the correct type to return? The general rule is to convert all of the arguments to the widest type, but here there's no clear "winner" between int
and unsigned int
. In the first case, unsigned int
must be being chosen as the result of operator+
, because I get a result of 2147483648
. In the second case, it must be choosing int
, because I get a result of -1
. Yet I don't see in the general case how this is decidable. Is this undefined behavior I'm seeing or something else?
This is outlined explicitly in ยง5/9:
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:
- If either operand is of type
long double
, the other shall be converted tolong double
.- Otherwise, if either operand is
double
, the other shall be converted todouble
.- Otherwise, if either operand is
float
, the other shall be converted tofloat
.- Otherwise, the integral promotions shall be performed on both operands.
- Then, if either operand is
unsigned long
the other shall be converted tounsigned long
.- Otherwise, if one operand is a
long int
and the otherunsigned int
, then if along int
can represent all the values of anunsigned int
, theunsigned int
shall be converted to along int
; otherwise both operands shall be converted tounsigned long int
.- Otherwise, if either operand is
long
, the other shall be converted tolong
.- Otherwise, if either operand is
unsigned
, the other shall be converted tounsigned
.[Note: otherwise, the only remaining case is that both operands are
int
]
In both of your scenarios, the result of operator+
is unsigned
. Consequently, the second scenario is effectively:
int result = static_cast<int>(us + static_cast<unsigned>(neg));
Because in this case the value of us + neg
is not representable by int
, the value of result
is implementation-defined – ยง4.7/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.