web-dev-qa-db-ja.com

Nullptrとは一体何ですか?

C++ 11には多くの新機能があります。 (少なくとも私にとっては)面白くて紛らわしいのが新しいnullptrです。

厄介なマクロNULLはもう必要ありません。

int* x = nullptr;
myclass* obj = nullptr;

それでも、私はnullptrがどのように機能するのかわかりません。例えば、 ウィキペディアの記事 と言う:

C++ 11では、区別されたNULLポインタ定数nullptrとして機能する新しい キーワード を導入することでこれを修正しています。これは type nullptr_t であり、これは暗黙的に変換が可能で、あらゆるポインタ型またはメンバへのポインタ型に相当します。 boolを除いて、暗黙的に変換可能であることや整数型に匹敵することはありません。

キーワードと型のインスタンスはどうですか。

また、nullptrが古き0より優れている別の例(ウィキペディアのもの以外)はありますか?

532
AraK

キーワードと型のインスタンスはどうですか。

これは驚くべきことではありません。 truefalseはどちらもキーワードであり、リテラルとしては型(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_castnullptr_tをどのポインタ型にも変換できません。可能であれば暗黙的な変換に依存するか、static_castを使用してください。

  • 規格はsizeof(nullptr_t)sizeof(void*)であることを要求しています。

377

From nullptr:型安全でクリアカットのNULLポインタ

新しいC++ 09 nullptrキーワードは、汎用のNULLポインタリテラルとして機能する右辺値定数を指定します。これは、バグのある、型の弱いリテラル0および悪名高いNULLマクロに代わるものです。したがって、nullptrは、30年以上にわたる当惑、あいまいさ、およびバグに終止符を打ちます。以下の節では、nullptr機能を紹介し、NULLと0の病気をどのように解決できるかを示します。

その他の参考文献

56
nik

複数の型へのポインタを受け取ることができる関数があるとき、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)  {  }
35
Motti

なぜC++ 11ではnullptrなのかそれは何ですか? NULLはなぜ不十分なのですか?

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の意味を知っている)、しかしより明示的なコンパイラ。ポインタ」

23
Gabriel Staples

nullptrはintなどのintegral typeに代入することはできませんが、pointer型のみに割り当てることができます。 int *ptrなどの組み込みポインタ型またはstd::shared_ptr<T>などのスマートポインタ

NULLintegral typeに割り当てることができ、pointerNULLの初期値としてもintの初期値としても使用できるので、pointer0に展開されたマクロなので、これは重要な違いです。

9
user633658

まあ、他の言語は型のインスタンスである予約語を持っています。 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)。

4
Mark Rushakoff

また、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
4

規格ではそのように指定されているため、キーワードです。 ;-)最新の公開草案(n2914)によると

2.14.7ポインタリテラル[Lex.nullptr]

pointer-literal:
nullptr

ポインタリテラルはキーワードnullptrです。これはstd::nullptr_t型の右辺値です。

暗黙的に整数値に変換されないので便利です。

3
KTC

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*)
}
3
Amit G.

0は、ポインタのキャストフリー初期化子として使用できる唯一の整数値でした。キャストなしでは、ポインタを他の整数値で初期化することはできません。 0は整数リテラルに構文的に類似したconxprシングルトンとして考えることができます。任意のポインタまたは整数を起動できます。しかし、驚くべきことに、それは明確な型がないことに気付くでしょう。それはintです。それでは、どうして0はポインタを初期化でき、1は初期化できないのでしょうか。実用的な答えは、ポインタのNULL値を定義する手段が必要であり、intからポインタへの暗黙的な直接変換はエラーが発生しやすいことです。このように、0は先史時代からの真の変態獣になりました。 nullptrは、ポインタを初期化するためのnull値の実数シングルトンのconstexpr表現として提案されました。整数を直接初期化するために使用することはできず、NULLを0で定義する際のあいまいさを排除します。nullptrは、std構文を使用してライブラリとして定義できますがNULLは、nullptrとして定義することを決定しない限り、nullptrを支持するようになりました。

0
Red.Wave