c++integermax

Why does (type)-1 return the max value for a type in C++?


While reading the source code for RakNet on GitHub, I came across the following in RakNetDefines.h.

typedef unsigned char RPCIndex;
const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1;
const int UNDEFINED_RPC_INDEX=((RPCIndex)-1);

When running the following code via an online C++ compiler:

#include <iostream>

typedef unsigned char RPCIndex;
const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1;
const int UNDEFINED_RPC_INDEX=((RPCIndex)-1);

int main() {
    std::cout << MAX_RPC_MAP_SIZE    << " ";
    std::cout << UNDEFINED_RPC_INDEX;
    return 0;
}

I get 254 255 as the output. While I know that 255 is (typically) the maximum value for an unsigned char in C++, I was shocked to see that simply putting the name of a type and subtracting one from it resulted in it's maximum value. Furthermore, writing (RPCIndex)-0 evaluates to 0. Why is this the case in C++? Is there a historical reason for it, is it just convenient, or am I missing something?


Solution

  • (RPCIndex)-1 is parsed as a C-style cast expression of the form (T)E where T is a type and E an unary expression.

    In this case T is the type RPCIndex and E is the expression -1 which is a unary minus operator applied to the 1 integer literal.

    The result is the value -1 converted to RPCIndex. Because RPCIndex is an unsigned integral type, the result is the representable value of RPCIndex congruent to -1 modulo 2**w where w is the width of the RPCIndex integer type. In other words, it produces 2**w-1, the largest representable value of the unsigned integer type.

    I guess you can see how (RPCIndex)-0 works...


    This -1 cast trick originates from C where there is no other way to name the maximum value of some aliased unsigned integer type without knowing what it aliases or what its maximum value is apriori.

    In C++ it can be expressed much more clearly what the intent is with std::numeric_limits from <limits>. The -1 trick is not necessary anymore:

    const int UNDEFINED_RPC_INDEX = std::numeric_limits<RPCIndex>::max();
    

    In general the code looks more like C than (modern) C++ code, although it behaves as expected in C++ as well.