暗黙の型変換の落とし穴

こんなツイートを見た。

答えはboolだ。その理由は、こういう一見自明なクイズは大抵一番正しそうに見える解答が誤答だから、ではなく、暗黙の型変換の優先準位によるものだ。

まず、"Hello!"の型はstd::stringではない。これは文字列リテラルなので、その型はconst char[]だ。上記のコードにおいては、これがまず先頭を指すポインタ(const char*)へ変換され、ポインタがboolへ変換される。std::stringは標準ライブラリといえどユーザー定義型であることは間違いないので、built-inな型のほうが選択され、これはboolになる。

いや、確かに、この挙動はクソだ。100人に聞いたら100人が、これはstd::stringになるべきだと答えるだろう。私もそう思う。この挙動はライブラリがどうにかできるものでもないし、ユーザー定義型への変換を優先する方が問題が生じる場合もありそうで、仕方ないと言えば仕方ない気もするが……。

挙動が直感的でないのに対して、解決策は単純だ。文字列リテラルからstd::stringへの暗黙の型変換に頼らず、型を明示すればよい。つまり、std::string_literals::operator""sを使えばよい。

using std::string_literals::operator""s;
std::variant<std::string, int, bool> v = "Hello!"s;

これなら型はstd::stringになる。

以前、あるプロジェクトが使っているクラスがboolへの型変換関数を持っていることを知らず、そのクラスがboolへ変換された後にintに変換されることでオーバーロードが謎の方向に解決されるという事案が発生して死んでいたことがある。今回の件ではそれを思い出した。