📌
整数型の比較 in C++20
C++は符号付きと符号なし整数の値の比較に問題を抱えています。
#include <iostream>
int main() {
std::cout << std::boolalpha;
int n = -1;
unsigned int m = 0;
std::cout << (n < m) << '\n'; // false
std::cout << (-1 < 0) << '\n'; // true
}
<
演算子の両辺のオペランドの型が異なっているので、暗黙変換の結果として両辺共にunsigned int
になり(多分)、その結果int
の-1
はunsigned int
の4294967295
なるので、上のコードのn < m
はfalse
となります。
なお、定数式ならばこれは意図通りになります。
この様に符号の異なる整数型の比較は危険なので、C++20の<=>
では符号付きと符号なし整数の比較はコンパイルエラーとなります(定数式以外)。
多くのコンパイラはこのような比較について警告を発しますが、多くの場合はどうしようもなかったり、実害が無かったりして無視されがちです。
C++20では、この様な比較を安全に行い、警告を消し去るべく整数型の比較関数が追加されました。
namespace std {
template <class T, class U>
constexpr bool cmp_equal(T t, U u) noexcept;
template <class T, class U>
constexpr bool cmp_not_equal(T t, U u) noexcept;
template <class T, class U>
constexpr bool cmp_less(T t, U u) noexcept;
template <class T, class U>
constexpr bool cmp_greater(T t, U u) noexcept;
template <class T, class U>
constexpr bool cmp_less_equal(T t, U u) noexcept;
template <class T, class U>
constexpr bool cmp_greater_equal(T t, U u) noexcept;
}
これらの比較関数は整数型の値に対して名前通りの比較を行うものです。そして、その2つの引数型の符号有無が異なっていたとしても先程の様な暗黙変換を行う事無く、その値による正しい比較を行うものです。
#include <iostream>
#include <utility>
int main() {
std::cout << std::boolalpha;
int n = -1;
unsigned int m = 0;
std::cout << std::cmp_less(n, m) << '\n'; // true
std::cout << std::cmp_less(-1, 0) << '\n'; // true
}
大変便利なので、C++20以降は積極的に活用しましょう!
ちなみに同時に、指定された整数値が指定した整数型で表現可能かどうかを判定するin_range
関数も追加されています。
#include <iostream>
#include <utility>
int main() {
std::cout << std::boolalpha;
std::cout << std::in_range<std::uint32_t>(-1) << '\n'; // false
std::cout << std::in_range<std::int32_t>(-1) << '\n'; // true
std::cout << std::in_range<std::int32_t>(4294967295) << '\n'; // false
std::cout << std::in_range<std::uint32_t>(4294967295) << '\n'; // true
}
Discussion