最終更新日時:
が更新

履歴 編集

非推奨だった bool 型に対するインクリメント演算子を削除(C++17)

概要

C++17ではbool型に対する前置および後置のoperator ++を削除する。

bool型に対する前置および後置のoperator ++とはC++98の時点で非推奨になっていた機能である。

具体的にどのような働きをするのかというと、以下のように値をtrueに書き換える機能をもつ。

int main()
{
  bool b = false;
  ++b; // => true
  ++b; // => true
}

ここで、前置のoperator ++は、以下のように置き換えられる:

int main()
{
  bool b = false;
  b = true; // => true
  b = true; // => true
}

一方後置のoperator ++を使う次のようなコードは、以下のようにC++14で標準ライブラリに導入されたstd::exchange()を利用して書き換えることができる。

#include <iostream>

void f(bool b)
{
  std::cout << ((b) ? "true" : "false") << std::endl;
}

int main()
{
  bool b = false;
  // 関数fには変数bの現在の値であるfalseの値が渡される
  f(b++); // => false
  std::cout << ((b) ? "true" : "false") << std::endl; // => true
}

#include <iostream>
#include <utility>

void f(bool b)
{
  std::cout << ((b) ? "true" : "false") << std::endl;
}
int main()
{
  bool b = false;
  f(std::exchange(b, true)); // => false
  std::cout << ((b) ? "true" : "false") << std::endl; // => true
}

仕様

これまで、opeartor ++の定義は、bool型のときはtrueに変更する、opeartor --の定義はbool型を除く、というように例外規定されていた(§ 8.2.6 expr.post.incr / § 8.3.2 expr.pre.incr)。
C++17ではこれらが削除され、opeartor ++の定義(§ 8.2.6 expr.post.incr / § 8.3.2 expr.pre.incr)に、bool型を除く、という例外規定が追加された。

前置のoperator ++operator +=の呼び出し(例えば++aa+=1)が等価にならない例に、bool型の場合、という文面があったが、C++17で削除された(§ 8 expr)。

また、組み込みのoperatorのリストのoperator ++に関する文面に、bool型を除く、という例外規定が追加された(§ 16.6 over.built)。

この機能を削除するに至った背景・経緯

この項は十分な出典が存在せず推測でしかないことに注意して読み進めてほしい。

もともとC++の前身であるC言語(ANSI C89)にはbool型は存在しなかった。そのために、真理値をint型で代用する例が見られた。

int main(void)
{
  int flag = 0;
  /* do something */
  if(flag){
    /* do something when flag is true*/
  }
  return 0;
}

つまり、非0をtrue、0をfalseとして扱う。ここで次のようなコードを見てみよう。

#include <iostream>
#include <vector>
int main()
{
  std::vector<int> v{};
  //append elements to v
  int flag = 0;
  for(auto&& i : v){
    if(flag++) std:cout << ',';
    std::cout << i << std::endl;
  }
}

これは、最初の要素以外のときは,という文字を要素の出力の前に行うことを期待している。しかし期待通りには動かない。
flagint型の最大値になったときif文の条件評価が行われることを考えよう。flagのインクリメントはオーバーフローするので未定義動作になるが、殆どの環境で2の補数表現を使っているため、int型の最小値になる。すると、これが1になるまでif文の中の式は実行されない。

これに起因するバグで少なくとも6つの過度の放射線被曝事故を引き起こし、3人が死亡した例がある。
Therac-25はカナダ原子力公社(AECL)とフランスCGR-MeV社によって開発・製造された放射線療法機器である。
この装置のソフトウェアのバグの一つに、条件変数を非0にする(=trueにする)ために、インクリメントを使っていたというものがあった。
条件変数はC++でいえばstd::uint8_t型で、つまり256回に1度オーバーフローを起こして値が0になるために、falseとして扱われた。
この結果ほかの条件変数の状態によっては25MeVという通常の100倍のβ線が射出されることがあった。

こうした事故を防ぐためなのかは不明だが、C++のbool型はインクリメントした際、常にtrueになるように定められていた。
しかし、そもそも上記のバグを防ぐには、インクリメントではなく単に固定値を代入するようにするべきであり、C++98の時点でdeprecatedになっていたと思われる。

C++14でstd::exchange()が導入されたことにより、唯一使いみちのあった後置のoperator++の必要性もなくなり、C++17で削除されたと推測される。

関連項目

参照