Integer Conversions and Safe Comparisons in C++20
cppstories.com
cppstories.com
https://github.com/titzer/virgil/blob/master/doc/tutorial/Fi...
One of the key things is that values are never silently truncated (other than 2's-complement wrap-around that is built-in to arithmetic) or values changed; only promotions. The only sane semantics for over-shifts (shifts larger than the size of the type) is to shift the bits out, like a window.
The upshot of all that is that Virgil has a pretty sane semantics for fixed-size integers, IMHO. Particularly comparisons. Comparing signed and unsigned ints is not UB and cannot silently give the wrong answer; integers are always compared on the same number line. It takes only a maximum of one extra comparison to achieve this. AFAICT this is what achieved by the std::cmp* referenced in the article.
With each standard revision, C++ is becoming more and more horrible as much as it strives to become a better and more useful language.
how can one implement this without a branch before literally any arithmetic operation?
In Erlang, you don't; the language was never made for that sort of speed.
C++20 IS fixing many complaints or design-choices made in the past.
In terms of the article it casually mentions in the end using
'''-Werror -Wall -Wextra''' The unsigned signed comparisons will fail to compile. Most serious projects use these flags anyway.
I wonder if clang-tidy will also catch this?
every language has its dark corner, none is perfect, same as C and C++. C and C++ are popular for decades is a fact, like it or not, data does not lie.
I'm interested in solving real problems, but C++ deserves trash talking for this reason alone.
[1] It's non-obvious to me why an implicit conversion with potential UB would ever be a good idea. If a programmer really did want a UB-having single-machine compare between a signed (but non-negative) int and an unsigned int, they could write an explicit static_cast and then compare. Such a static_cast would be the UB-having nop that unlocked a non-UB comparison. You'd get the exact machine code that you wanted, but you had to opt into UB. Bad default that the short, intuitive-looking code has UB.
> If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). ]
It looks like C++20 updated this so that conversion both ways is defined in a way that preserves two's complement bit-patterns, which you can see here: https://eel.is/c++draft/conv#integral-3
Most serious projects are opting in to being unable to compile on future compilers whenever new warnings are added?
Thus comparing unsigned short and short will usually (if short is narrower than int) do the right thing. C++Insights (which I didn't know! This is the real pearl of this article.) agrees with me [1].
[0] https://en.cppreference.com/w/cpp/language/operator_arithmet...
[1] https://cppinsights.io/lnk?code=I2luY2x1ZGUgPGNzdGRpbz4KCmlu...
> If the operand passed to an arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion.
Clicking "integral promotion" leads here: https://en.cppreference.com/w/cpp/language/implicit_conversi...
I thought C and C++ were advertised as language that don't do a lot of hand-holding and requires programmers to know what they are doing, and many aspects of it such as memory management really reflects that. So why is there implicit integral type conversion? Why not require programmer to always explicitly convert like Rust does?