C++ 11には多くの新機能があります。 (少なくとも私にとっては)面白くて紛らわしいのが新しいnullptr
です。
厄介なマクロNULL
はもう必要ありません。
int* x = nullptr;
myclass* obj = nullptr;
それでも、私はnullptr
がどのように機能するのかわかりません。例えば、 ウィキペディアの記事 と言う:
C++ 11では、区別されたNULLポインタ定数nullptrとして機能する新しい キーワード を導入することでこれを修正しています。これは type nullptr_t であり、これは暗黙的に変換が可能で、あらゆるポインタ型またはメンバへのポインタ型に相当します。 boolを除いて、暗黙的に変換可能であることや整数型に匹敵することはありません。
キーワードと型のインスタンスはどうですか。
また、nullptr
が古き0
より優れている別の例(ウィキペディアのもの以外)はありますか?
キーワードと型のインスタンスはどうですか。
これは驚くべきことではありません。 true
とfalse
はどちらもキーワードであり、リテラルとしては型(bool
)を持ちます。 nullptr
は ポインタリテラル であり、std::nullptr_t
型であり、値です(&
を使用してアドレスを取得することはできません)。
ポインタ変換についての4.10
は、型std::nullptr_t
のprvalueがNULLポインタ定数であり、整数のNULLポインタ定数はstd::nullptr_t
に変換できることを示しています。反対方向は許されません。これにより、ポインタと整数の両方の関数をオーバーロードし、ポインタバージョンを選択するためにnullptr
を渡すことができます。 NULL
または0
を渡すと、int
バージョンが混乱して選択されます。
整数型へのnullptr_t
のキャストはreinterpret_cast
を必要とし、整数型への(void*)0
のキャストと同じ意味を持ちます(マッピング実装が定義されている)。 reinterpret_cast
はnullptr_t
をどのポインタ型にも変換できません。可能であれば暗黙的な変換に依存するか、static_cast
を使用してください。
規格はsizeof(nullptr_t)
がsizeof(void*)
であることを要求しています。
From nullptr:型安全でクリアカットのNULLポインタ :
新しいC++ 09 nullptrキーワードは、汎用のNULLポインタリテラルとして機能する右辺値定数を指定します。これは、バグのある、型の弱いリテラル0および悪名高いNULLマクロに代わるものです。したがって、nullptrは、30年以上にわたる当惑、あいまいさ、およびバグに終止符を打ちます。以下の節では、nullptr機能を紹介し、NULLと0の病気をどのように解決できるかを示します。
その他の参考文献
template
複数の型へのポインタを受け取ることができる関数があるとき、NULL
でそれを呼び出すことはあいまいです。これが現在回避されている方法は、intを受け入れて、それがNULL
であると仮定することによって非常に厄介です。
template <class T>
class ptr {
T* p_;
public:
ptr(T* p) : p_(p) {}
template <class U>
ptr(U* u) : p_(dynamic_cast<T*>(u)) { }
// Without this ptr<T> p(NULL) would be ambiguous
ptr(int null) : p_(NULL) { assert(null == NULL); }
};
C++11
ではnullptr_t
をオーバーロードすることができるので、ptr<T> p(42);
は実行時のassert
ではなくコンパイル時のエラーになります。
ptr(std::nullptr_t) : p_(nullptr) { }
C++のエキスパート Alex Allainがここで完璧に言っています :
「...次の2つの関数宣言があると想像してください。
void func(int n);
void func(char *s);
func( NULL ); // guess which function gets called?
2番目の関数が呼び出されるように見えますが、結局のところ、ポインタであると思われるものを渡しています - 実際に呼び出されるのは最初の関数です。問題は、NULLが0で、0が整数なので、最初のバージョンのfuncが代わりに呼び出されることです。こういうことです、そう、いつも起こるわけではありませんが、起こると、非常にイライラして混乱します。何が起こっているのか詳細がわからなければ、コンパイラのバグのように見えるかもしれません。 コンパイラのバグのように見える言語機能は、あなたが望むものではありません。
nullptrを入力してください。 C++ 11では、nullptrはNULLポインタを表すために使用できる(そしてそうすべきです)新しいキーワードです。つまり、以前にNULLを書いていたところはどこでも、代わりにnullptrを使うべきです。 プログラマにとっては明らかではない、(誰もがNULLの意味を知っている)、しかしより明示的なコンパイラ。ポインタ」
nullptr
はintなどのintegral type
に代入することはできませんが、pointer
型のみに割り当てることができます。 int *ptr
などの組み込みポインタ型またはstd::shared_ptr<T>
などのスマートポインタ
NULL
はintegral type
に割り当てることができ、pointer
はNULL
の初期値としてもint
の初期値としても使用できるので、pointer
は0
に展開されたマクロなので、これは重要な違いです。
まあ、他の言語は型のインスタンスである予約語を持っています。 Python、例えば:
>>> None = 5
File "<stdin>", line 1
SyntaxError: assignment to None
>>> type(None)
<type 'NoneType'>
None
は通常、初期化されていないものに使用されますが、同時にNone == 0
などの比較はfalseであるため、これは実際にはかなり近い比較です。
一方、プレーンCでは、NULL
は0を返す単なるマクロであるため、NULL == 0
はtrue IIRCを返します。これは常に無効なアドレスです(AFAIK)。
また、
nullptr
が古き良き0より優れているという別の例(ウィキペディアのもの以外)はありますか?
はい。これは、私たちの製品コードで発生した(単純化された)実世界の例です。 gccが異なるレジスタ幅を持つプラットフォームへのクロスコンパイル時に警告を出すことができたので、それは際立っていました(それでもx86_64からx86へクロスコンパイルする時だけなぜ正確にわからない、warning: converting to non-pointer type 'int' from NULL
に警告します)
このコードを考えてください(C++ 03):
#include <iostream>
struct B {};
struct A
{
operator B*() {return 0;}
operator bool() {return true;}
};
int main()
{
A a;
B* pb = 0;
typedef void* null_ptr_t;
null_ptr_t null = 0;
std::cout << "(a == pb): " << (a == pb) << std::endl;
std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning
std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes
std::cout << "(a == null): " << (a == null) << std::endl;
}
それはこの出力をもたらします:
(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
規格ではそのように指定されているため、キーワードです。 ;-)最新の公開草案(n2914)によると
2.14.7ポインタリテラル[Lex.nullptr]
pointer-literal: nullptr
ポインタリテラルはキーワード
nullptr
です。これはstd::nullptr_t
型の右辺値です。
暗黙的に整数値に変換されないので便利です。
Intとchar *の両方を取るためにオーバーロードされている関数(f)があるとしましょう。 C++ 11より前のバージョンでは、NULLポインタを使用してこれを呼び出したいが、NULL(つまり値0)を使用した場合は、intに対してオーバーロードされたものを呼び出します。
void f(int);
void f(char*);
void g()
{
f(0); // Calls f(int).
f(NULL); // Equals to f(0). Calls f(int).
}
これはおそらくあなたが望んでいたものではありません。 C++ 11はこれをnullptrで解決します。今、あなたは以下を書くことができます:
void g()
{
f(nullptr); //calls f(char*)
}
0は、ポインタのキャストフリー初期化子として使用できる唯一の整数値でした。キャストなしでは、ポインタを他の整数値で初期化することはできません。 0は整数リテラルに構文的に類似したconxprシングルトンとして考えることができます。任意のポインタまたは整数を起動できます。しかし、驚くべきことに、それは明確な型がないことに気付くでしょう。それはint
です。それでは、どうして0はポインタを初期化でき、1は初期化できないのでしょうか。実用的な答えは、ポインタのNULL値を定義する手段が必要であり、int
からポインタへの暗黙的な直接変換はエラーが発生しやすいことです。このように、0は先史時代からの真の変態獣になりました。 nullptr
は、ポインタを初期化するためのnull値の実数シングルトンのconstexpr表現として提案されました。整数を直接初期化するために使用することはできず、NULL
を0で定義する際のあいまいさを排除します。nullptr
は、std構文を使用してライブラリとして定義できますがNULL
は、nullptr
として定義することを決定しない限り、nullptr
を支持するようになりました。