NULLとnullptrが混在していていいのか? in C++
ねににみみずです。ヌルポインタについての雑記です。
空値
C++には「空のポインタ」を示すNULLマクロがあります。そして、同じく「空のポインタ」を示すnullptrというキーワードもあります。
プリプロセスで整数型の0に置き換えられるNULLマクロでは「ポインタ型である」という情報が正確に伝達できないため(具体的にはオーバーロードの引数が整数型とポインタ型のときなどで困るらしい)、「空のポインタを表す型」としてstd::nullptr_t型およびnullptrキーワードが追加された、という背景があるそうです。
人間目線でもコンピュータ目線でも意図が明確である
ため最近の書籍などでは空のポインタはnullptrにするようになっていますが、個人サイトで紹介されているちょっと前の解説やソースコードでは未だにNULLマクロが顔を見せます。
混在可?
そこで、「NULLマクロとnullptrは混在していいのか?」という疑問が生じました。具体的には以下の感じです。
// ポインタがあったとして...
Hoge* hoge;
// NULLを代入した後に
hoge = NULL;
if (hoge == nullptr) {
// ここは通れるのか?
}
/// nullptrを代入した後に
hoge = nullptr;
if (hoge == NULL) {
// ここは通れるのか?
}
そもそもNULLマクロとnullptrは定義が異なるわけで、一緒くたに扱うとバグの元になったりはしないのでしょうか?
チームメンバーやライブラリ側とのすれ違いによってバグが混入するようでは大変です(チームに関しては規約で決めとけよって話なんですが)。
無問題
全くもってOKでした。nullptrのほうのリファレンスに、具体的な記載がありました。
・nullptr_t 型の値は整数型に reinterpret_cast でキャストできる。その結果は、 (void*)0 を整数型に変換することと同じとなる。
nullptrを整数にreinterpret_castすることは0をvoid*型にキャストしたものを再度整数型に変換することと同じ、つまり0と等しくなると考えてよさそうです。
ビット表現の変換
static_cast や dynamic_cast では型の変換と同時にビット表現を変換しますが、 reinterpret_cast ではビット表現を変更せずに型だけを変更します。
整数型と浮動小数点数型などビット表現が異なる型の変換を行うと、 値の変換の有無によって挙動の差異が生じます。
例えばこれはマイクロソフトの回答。
該当部分を引用すると、
例: nullptr と 0 を相互に置き換えて使用する
次のコード例は、ネイティブ ポインター上で nullptr と 0 を相互に置き換えて使用できることを示しています。
C++
// mcpp_nullptr_1.cpp
// compile with: /clr
class MyClass {
public:
int i;
};
int main() {
MyClass * pMyClass = nullptr;
if ( pMyClass == nullptr)
System::Console::WriteLine("pMyClass == nullptr");
if ( pMyClass == 0)
System::Console::WriteLine("pMyClass == 0");
pMyClass = 0;
if ( pMyClass == nullptr)
System::Console::WriteLine("pMyClass == nullptr");
if ( pMyClass == 0)
System::Console::WriteLine("pMyClass == 0");
}
Output
pMyClass == nullptr
pMyClass == 0
pMyClass == nullptr
pMyClass == 0
NULLマクロは0に置き換えられるので、NULLとnullptrは互換できると言えそうです。
まあ後から追加されたnullptrがNULLと同じ値を返すように出来ているのは当然といえば当然なんですが、疑問はとりあえず解決です。よかった。