Search code examples
c++undefined-behaviorinteger-overflowunsigned-integer

If I decrement `std::size_t(0)` is that guaranteed to be equal to `std::size_t(-1)`?


Here's evidence that it is:

inline
constexpr std::size_t prev(std::size_t i) {
    --i;
    return i;
}

int main() {
    static const std::size_t i = 0;
    static_assert(prev(i) == std::size_t(-1), "Decrementing should give     std::size_t(-1)");    
    return 0;
}

That compiles happily with -std=c++14.

I came upon this because I had a loop indexing over a std::vector and wanted to loop backward, so I changed it to

for (std::size_t i = std::min(idx, v.size() - 1); i != std::size_t(-1); --i) { ... }

Now, I realize I could use std::vector::reverse_iterator, but my real question now is, is the behavior I'm expecting well-defined?


Solution

  • Yes, this behavior is guaranteed.

    std::size_t is an unsigned integer type. Arithmetic on unsigned integers always has well defined semantics:

    Unsigned integer arithmetic is always performed modulo 2n where n is the number of bits in that particular integer.

    Specifically considering the built-in pre-decrement and post-decrement operators:

    [T]he expression --x is exactly equivalent to x -= 1. ...
    [T]he expression x-- modifies the value of its operand as if by evaluating x -= 1

    So the decrement operator does perform an arithmetic operation.