yohhoyの日記

技術的メモをしていきたい日記

C++標準I/Oストリームと暗黙型変換の落とし穴

C++標準ライブラリのI/Oストリームにある暗黙のユーザ定義変換(user-defined conversion)のせいで、プログラマが意図しない動作を引き起こすケースがある。

#include <iostream>

int main()
{
  std::cout << std::cin; // ??
}

上記コードは正常にコンパイル可能。gcc 4.6.3でコンパイルしたときの実行結果の例。

$ g++ -W -Wall -Wextra -pedantic source.cpp
$ ./a.out
0x6c504048

ここではbasic_istream<char>型のcinオブジェクトに対して、その公開基底クラスbasic_ios<char>のユーザ定義変換関数operator void*() const暗黙的に呼び出されている。C++03 27.4.4.3/p1より引用。

operator void*() const;
Returns: If fail() then a null pointer; otherwise some non-null pointer to indicate success.

basic_ostream<char>型のcoutオブジェクトに対して、同クラスのメンバ関数operator<<(const void* p)が呼び出され、最終的にポインタ値として出力される。結局のところ下記コードと等価。

std::cout << static_cast<void*>( static_cast<std::ios&>( std::cin ) );

C++11において前述のユーザ定義変換関数(basic_ios<〜>void*)は削除され、代わりにユーザ定義変換関数explicit operator bool() constが追加された。N3337 27.5.5.4/p1より引用。(また、C.2.15で互換性について言及されている。)

explicit operator bool() const;
Returns: !fail().

注意:ユーザ定義変換でのexplicit指定が可能なのはC++11から。C++98/03ではill-formed。


関連URL: