整数型の比較 in C++20

4 min読了の目安(約2400字TECH技術記事

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
}

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

<演算子の両辺のオペランドの型が異なっているので、暗黙変換の結果として両辺共にunsigned intになり(多分)、その結果int-1unsigned int4294967295なるので、上のコードのn < mfalseとなります。

なお、定数式ならばこれは意図通りになります。

この様に符号の異なる整数型の比較は危険なので、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
}

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

大変便利なので、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 
}

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ