C++11 では、NULL に代わるものとして nullptr キーワードが追加されています。
この話を聞くと「NULL じゃだめなの?」と一度は思う人は少なく無いと思います。
そこで、今回は NULL と nullptr の違いについて書いてみたいと思います。
NULL じゃ出来ないことがある
今更言うことでもないですが、NULL は以下のようにマクロで定義されています。
#define NULL 0
マクロということは、NULL なんて大層な扱いになっていても 0 以外の何者でもないということです。
そこで、以下のコードと実行結果を見てみます。
#include <iostream>
using namespace std;
void Func(void * p) {
cout << __FUNCSIG__ << endl;
}
void Func(int v) {
cout << __FUNCSIG__ << endl;
}
int main()
{
Func(10);
Func(NULL);
return 0;
}
void __cdecl Func(int)
void __cdecl Func(int)
NULL を指定しても Func(void * v) ではなく、Func(int v) が呼び出されてます。
そりゃそうですよね。
コンパイラから見たら NULL はポインタではなく、ただの 0 なんですから。
もし、Func(void * v) を呼びたいのであれば
Func((void*)NULL);
のように明示的にキャストしてやる必要があります。
が、NULL ポインタを明示的にキャストするのはなんともおかしな話ですし、
何より「それくらい勝手にやってくれよ!」って思いますよね。
#nullptr だからこそできる事がある
C++11 で追加された nullptr キーワードは、まさしくそのために用意されたものです。
以下が NULL を nullptr に置き換えたコードと実行結果です。
#include <iostream>
using namespace std;
void Func(void * p) {
cout << __FUNCSIG__ << endl;
}
void Func(int v) {
cout << __FUNCSIG__ << endl;
}
int main()
{
Func(10);
Func(nullptr);
return 0;
}
void __cdecl Func(int)
void __cdecl Func(void *)
nullptr を指定したら、見事に Func(void *) が呼び出されています。
このように、nullptr を指定すると、きちんとポインタとして扱ってくれます。
nullptr は nullptr_t 型
また、nullptr はポインタとして扱われますが、 型としては std::nullptr_t 型 という扱いになっています。
どういう事かというと、それは以下のコードと実行結果を見てもらえればわかるかと思います。
#include <iostream>
using namespace std;
void Func(void * p) {
cout << __FUNCSIG__ << endl;
}
void Func(int v) {
cout << __FUNCSIG__ << endl;
}
void Func(nullptr_t p) {
cout << __FUNCSIG__ << endl;
}
int main()
{
Func(10);
Func(nullptr);
return 0;
}
void __cdecl Func(int)
void __cdecl Func(nullptr)
このように、ポインタを引数として取る関数と nullptr_t を引数として取る関数がオーバーロードされている場合は、
nullptr の本来の型である nullptr_t が優先して呼び出されることになります。