C ++ nullptr実装はどのように機能しますか?


13

仕組みを知りたいnullptrです。規格N4659およびN4849は次のように述べています。

  1. タイプが必要std::nullptr_tです。
  2. そのアドレスを取ることはできません。
  3. これは、ポインターおよびメンバーへのポインターに直接変換できます。
  4. sizeof(std::nullptr_t) == sizeof(void*);
  5. への変換boolfalseです。
  6. その値は、と同じように整数型に変換できます(void*)0が、逆方向には変換できません。

したがって、基本的にはと同じ意味を持つ定数ですが、(void*)0型が異なります。std::nullptr_t私のデバイスでの実装を見つけました。それは次のとおりです。

#ifdef _LIBCPP_HAS_NO_NULLPTR

_LIBCPP_BEGIN_NAMESPACE_STD

struct _LIBCPP_TEMPLATE_VIS nullptr_t
{
    void* __lx;

    struct __nat {int __for_bool_;};

    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t() : __lx(0) {}
    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t(int __nat::*) : __lx(0) {}

    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR operator int __nat::*() const {return 0;}

    template <class _Tp>
        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
        operator _Tp* () const {return 0;}

    template <class _Tp, class _Up>
        _LIBCPP_INLINE_VISIBILITY
        operator _Tp _Up::* () const {return 0;}

    friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator==(nullptr_t, nullptr_t) {return true;}
    friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator!=(nullptr_t, nullptr_t) {return false;}
};

inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t __get_nullptr_t() {return nullptr_t(0);}

#define nullptr _VSTD::__get_nullptr_t()

_LIBCPP_END_NAMESPACE_STD

#else  // _LIBCPP_HAS_NO_NULLPTR

namespace std
{
    typedef decltype(nullptr) nullptr_t;
}

#endif  // _LIBCPP_HAS_NO_NULLPTR

ただし、前半の方が興味があります。それはポイント1-5を満たしているようですが、サブクラス__natとそれに関連するすべてのものがある理由はわかりません。また、積分変換で失敗する理由も知りたいです。

struct nullptr_t2{
    void* __lx;
    struct __nat {int __for_bool_;};
     constexpr nullptr_t2() : __lx(0) {}
     constexpr nullptr_t2(int __nat::*) : __lx(0) {}
     constexpr operator int __nat::*() const {return 0;}
    template <class _Tp>
         constexpr
        operator _Tp* () const {return 0;}
    template <class _Tp, class _Up>
        operator _Tp _Up::* () const {return 0;}
    friend  constexpr bool operator==(nullptr_t2, nullptr_t2) {return true;}
    friend  constexpr bool operator!=(nullptr_t2, nullptr_t2) {return false;}
};
inline constexpr nullptr_t2 __get_nullptr_t2() {return nullptr_t2(0);}
#define nullptr2 __get_nullptr_t2()

int main(){
    long l  = reinterpret_cast<long>(nullptr);
    long l2 = reinterpret_cast<long>(nullptr2); // error: invalid type conversion
    bool b  = nullptr; // warning: implicit conversion
                       // edditor error: a value of type "std::nullptr_t" cannot be used to initialize an entity of type "bool"
    bool b2 = nullptr2;
    if (nullptr){}; // warning: implicit conversion
    if (nullptr2){};
};

2
nullptr_t基本的なタイプです。どのようにint実装されていますか?
LF

9
#ifdef _LIBCPP_HAS_NO_NULLPTR。これは、コンパイラがを提供しない場合のベストエフォートの回避策のようnullptrです。
クリス

5
@Fullfungo標準では、これnullptr_tは基本的なタイプであると述べています。クラス型として実装しても、適合実装にはなりません。chrisのコメントを参照してください。
LF

1
@LF規格では、基本的な型がクラス型ではないことを技術的に要求していますか?
eerorika

2
@eerorika:is_classそしてis_null_pointer、同じ型に対して両方を真にすることはできません。特定のタイプに対してtrueを返すことができるのは、1つの基本タイプのカテゴリー関数の1つだけです。
Nicol Bolas

回答:


20

nullptrがどのように機能するか知りたいです。

それは可能な限り単純な方法で機能します:fiatによって。これは、C ++標準が機能すると言っているため機能し、C ++標準は、実装がその方法で機能せる必要があると言っているため、機能します。

C ++言語の規則を使用して実装することは不可能であることを認識することが重要std::nullptr_tです。型のnullポインター定数からポインターへの変換std::nullptr_tは、ユーザー定義の変換ではありません。つまり、ヌルポインター定数からポインターに移動し、ユーザー定義の変換を介して他の型に変換することができます。これらはすべて、1つの暗黙的な変換シーケンスで行われます。

nullptr_tクラスとして実装する場合は不可能です。変換演算子はユーザー定義の変換を表し、C ++の暗黙的な変換シーケンスルールでは、そのようなシーケンスで複数のユーザー定義の変換を許可していません。

あなたが投稿したコードはの素晴らしい近似ですがstd::nullptr_t、それ以外の何物でもありません。タイプの正当な実装ではありません。これはおそらく、コンパイラがを適切にサポートする前の古いバージョンのコンパイラ(下位互換性のために残されたもの)によるものでしたstd::nullptr_t。これは#defines nullptrであることがわかりますが、C ++ 11はそれnullptrがマクロではなくキーワードであると述べています。

C ++がorをstd::nullptr_t実装できないのと同じように、C ++はを実装できません。それらを実装できるのは実装だけです。これが「基本的なタイプ」になる理由です。言語の一部ですintvoid*


その値は(void *)0と同じように整数型に変換できますが、後方には変換できません。

nullポインタ定数から整数型への暗黙的な変換はありません。から0整数型への変換がありますが、それは整数リテラルのゼロ、つまり整数だからです。

nullptr_t(を介して)整数型にキャストできますreinterpret_castが、暗黙的にポインタとに変換できるだけboolです。


4
@Wyck: " fiat "
Nicol

「C ++言語の規則を使用してstd :: nullptr_tを実装することは不可能」とはどういう意味ですか?それは、C ++コンパイラをC ++自体で完全に記述できないことを意味しますか(私は推測していません)?
ノーザン

3
@northerner:つまり、に必要な動作とまったく同じ型を作成することはできませんstd::nullptr_t。に必要な動作とまったく同じ型を記述できないのと同じようにint。あなたは近づくことはできますが、それでも大きな違いがあります。そして、私はis_classあなたのタイプがユーザー定義であることを公開するような特性検出器について話していません。基本的な型に必要な動作については、言語の規則を使用して単純にコピーできないものがあります。
Nicol Bolas

1
ただの言い回し。「C ++は実装できませんnullptr_t」と言うと、あまりに広範に話しすぎます。そして、「実装だけがそれを実装できる」と言っても、問題を混乱させるだけです。あなたが意味することはつまりnullptr_t、「実装することができないC ++ライブラリ内の基本言語のそれの一部だ。
スペンサー

1
@スペンサー:いいえ、私が言ったことを正確に意味しました:C ++言語を使用して、std::nullptr_t必要なすべてのことを行う型を実装することはできません。C ++と同様に、言語intは必要なすべてのことを行う型を実装できません。
Nicol Bolas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.