20

Consider the following code:

template<bool> class StaticAssert;
template<> class StaticAssert<true> {};
StaticAssert< (-1 < sizeof(int)) > xyz1; // Compile error
StaticAssert< (-1 > sizeof(int)) > xyz2; // OK

Why is -1 > sizeof(int) true?

  1. Is it true that -1 is promoted to unsigned(-1) and then unsigned(-1) > sizeof(int).
  2. Is it true that -1 > sizeof(int) is equivalent to -1 > size_t(4) if sizeof(int) is 4. If this is so why -1 > size_t(4) is false?

Is this C++ standard comformant?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Alexey Malistov
  • 26,407
  • 13
  • 68
  • 88

4 Answers4

15

The following is how standard (ISO 14882) explains abort -1 > sizeof(int)

Relational operator `>' is defined in 5.9 (expr.rel/2)

The usual arithmetic conversions are performed on operands of arithmetic or enumeration type. ...

The usual arithmetic conversions is defined in 5 (expr/9)

... The pattern is called the usual arithmetic conversions, which are defined as following:

  • If either operand is of type long double, ...
  • Otherwise, if either operand is dobule, ...
  • Otherwise, if either operand is float, ...
  • Otherwise, the integral promotions shall be performed on both operands.
  • ...

The integral promotions is defined in 4.5 (conv.prom/1)

An rvalue of type char, signed char, unsigned char, short int, or unsigned short int can be converted to an rvalue of type int if int can represent all the values of the source type; otherwise, the source rvalue can be converted to an rvalue of type unsigned int.

The result of sizeof is defined in 5.3.3 (expr.sizeof/6)

The result is a constant of type size_t

size_t is defined in C standard (ISO 9899), which is unsigned integer type.

So for -1 > sizeof(int), the > triggers usual arithmetic conversions. The usual arithmetic conversion converts -1 to unsigned int because int cannot represent all the value of size_t. -1 becomes a very large number depend on platform. So -1 > sizeof(int) is true.

czchen
  • 5,850
  • 2
  • 26
  • 17
  • 2
    It could just be a typo but `size_t` is _an_ unsigned integer type and it doesn't have to be the case that `int` can't represent all the values of `size_t` (`size_t` might be `unsigned short`), although it obviously can't on the question asker's platform. – CB Bailey Jun 23 '10 at 11:03
  • 2
    `(unsigned T)-1` isn't just a large value, it's *the* largest value `unsigned T` can hold. – GManNickG Jun 23 '10 at 23:22
  • 1
    I'm well aware what the standard allows. :) -1 is always the largest, read the conversion rules. Or this http://stackoverflow.com/questions/809227/is-it-safe-to-use-1-to-set-all-bits-to-true/809341#809341 – GManNickG Jun 25 '10 at 15:29
  • 1
    @GMan Thank for your help. I misunderstood the description in standard. (Remove wrong comment) – czchen Jul 02 '10 at 01:21
14

Because unsigned is stronger then signed and -1 converted to unsigned value as of size_t , so actually -1 == 0xFFFFFFFF > 4

This is how it should work according to C++ standard

Artyom
  • 31,019
  • 21
  • 127
  • 215
  • doesn't compilers issue warnings for such cases ? – kriss Jun 23 '10 at 09:32
  • @kriss - Different compilers issue different warnings. Also warnings can be supressed via compiler command-line options, and/or by pragmas in the source code; and/or then can be ignored by the programmer. – ChrisW Jun 23 '10 at 09:36
  • Only when One's or Two's complement is used (not sure which here). – rubenvb Jun 23 '10 at 12:25
  • 1
    @rubenvb: Doesn't matter: `unsigned(-1) == UINT_MAX` per the standard, everywhere. – MSalters Jun 23 '10 at 14:13
  • @Artyom: `unsigned is stronger then signed`. What is `stronger`? Standard does not define this term. – Alexey Malistov Jun 23 '10 at 14:28
4

because -1 gets casted to size_t and this is an unsigned data type - so (size_t)-1 == 4294967295 (on a 32bit system) which is definitely larger than 4

if you add -Wall to the gcc settings for example you get a warning that you are comparing a signed and an unsigned data type

Wolf
  • 9,679
  • 7
  • 62
  • 108
Nikolaus Gradwohl
  • 19,708
  • 3
  • 45
  • 61
  • Is it really safe to assume that sizeof(size_t) >= sizeof(int) - IOW: is it standardized? – Wolf Sep 13 '17 at 09:53
2

It's simple and sad. In C/C++:

  1. most of the time, unsigned integer types have the semantic of modular integers (they represent equivalence classes)
  2. comparisons of unsigned integer types have the semantic of usual integer ordering, so that 1U < 2U (IOW 0U is the smallest unsigned value)
  3. sizeof has type size_t
  4. size_t is an unsigned integer type
  5. Point (1) implies that mixed arithmetic computations involving a signed and an unsigned integer are done in unsigned, modular arithmetic: this is the only possibility without violating "unsigned mean modular" rule. It's trivial to convert an integer to the equivalence class of integers equivalent to it. (Whereas going the other way requires the choice of an integer to represent the equivalence class.)
  6. Point (5) implies that -1 < 1U is interpreted as unsigned(-1) < 1U, and unsigned(-1) = - 1U, and obviously - 1U < 1U, so -1 < 1U is true.
  7. Points (1,3,4) imply that sizeof something acts (mostly) as an equivalent class (!!!).
  8. All this imply that -1 < sizeof something

The conclusion: this is a design error inherited from C.

Rule:

Only use unsigned types for modular arithmetic, bits manipulations (&, |, ^, <<, >>, ~ operators), byte manipulations (unsigned char means "byte" in C/C++), and characters (unsigned char means character in C/C++).

Do not use unsigned types to do arithmetic.

If a function expects an integer value that should never be negative, take a signed integer, and optionally check in the function that the value is in range.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • I found point (6) a bit confusing, maybe `==` included in *`unsigned(-1)` = `- 1U`* would be better – Wolf Sep 13 '17 at 10:03