こんなツイートを見た。
#Thursdaysurvey on this code:
— Meeting C++ (@meetingcpp) September 14, 2017
> variant<string, int, bool> v = "Hello!"; <
What type is contained in v?#cpp#cplusplus
答えは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
に変換されることでオーバーロードが謎の方向に解決されるという事案が発生して死んでいたことがある。今回の件ではそれを思い出した。