このページはC++20に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
ポインタ型(メンバポインタも含む)からbool
型への変換が、縮小変換として規定されるようになる。
struct A {
bool b;
};
int main() {
int *p = nullptr;
A a{p}; // NG、{}初期化では縮小変換は許可されない
}
これはC++17に対する欠陥報告であるため、実装済みのコンパイラにおいてはC++17モード(-std=c++17を指定した場合など)でコンパイルした場合にも適用される。
この機能が必要になった背景・経緯
C++17で導入されたstd::variant
には当初、const char*
からbool
への暗黙変換によって意図しない構築がなされるバグがあった。
std::variant<std::string, bool> x = "abc"; // boolを保持して構築されてしまう
この他にも構築・代入時に縮小変換が行われてしまう事から同様の問題があり、それはC++20においてP0608R3によって解決された。そこでは、構築・代入時の縮小変換を禁止するとともに、bool
に変換可能な型をbool
に変換することを禁止することで問題に対処していた。
しかしそれによって、bool
に変換可能なユーザー定義型をbool
値として構築・代入することができなくなった。
std::bitset<4> b("0101");
std::variant<bool, int> v = b[1]; // intを保持して構築されてしまう
std::bitset
の非const
なoperator[]
はbool
型へ暗黙変換可能なプロキシオブジェクトを返す。
std::variant
の構築・代入時に縮小変換が起こることを検出して防止することはライブラリレベルで可能だったが、ポインタ型からbool
への変換を縮小変換として扱うということはライブラリレベルでは実装できなかったためbool
への変換全体を禁止せざるを得ず、このような問題が発生していた。
これらの問題の解決のために、ポインタ型からbool
型への変換は縮小変換であると規定することになった。これによって、std::variant
の構築・代入時のポインタ型からbool
型への変換は縮小変換の一種として検出され禁止されるようになり、上記のコードは意図どおりに動作するようになる。
std::variant<std::string, bool> x = "abc"; // std::stringを保持して構築
std::bitset<4> b("0101");
std::variant<bool, int> v = b[1]; // boolを保持して構築
{}
初期化では縮小変換が禁止されているためこれは破壊的変更となるが、そのような変換は多くの場合バグの可能性が高いこと、MSVCは非リテラルのポインタからbool
への変換を縮小変換として扱っていたことなどから、影響は少なくメリットの方が大きいと判断されたようだ。