Bagaimana cara kerja implementasi c ++ nullptr?

13

Saya ingin tahu cara nullptrkerjanya. Standar N4659 dan N4849 mengatakan:

  1. harus memiliki tipe std::nullptr_t;
  2. Anda tidak dapat mengambil alamatnya;
  3. itu dapat langsung dikonversi ke pointer dan pointer ke anggota;
  4. sizeof(std::nullptr_t) == sizeof(void*);
  5. konversi menjadi booladalah false;
  6. nilainya dapat dikonversi ke tipe integral secara identik dengan (void*)0, tetapi tidak mundur;

Jadi pada dasarnya konstanta dengan makna yang sama (void*)0, tetapi memiliki tipe yang berbeda. Saya telah menemukan implementasi std::nullptr_tpada perangkat saya dan itu adalah sebagai berikut.

#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

Saya lebih tertarik pada bagian pertama. Tampaknya memuaskan poin 1-5, tapi saya tidak tahu mengapa ia memiliki subclass __nat dan segala sesuatu yang berkaitan dengannya. Saya juga ingin tahu mengapa gagal pada konversi integral.

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){};
};
Fullfungo
sumber
2
nullptr_tadalah tipe fundamental. Bagaimana intpenerapannya?
LF
9
Catatan #ifdef _LIBCPP_HAS_NO_NULLPTR. Ini sepertinya solusi terbaik saat ketika kompiler tidak menyediakan nullptr.
chris
5
@ Fullfungo Standar mengatakan itu nullptr_tadalah tipe fundamental. Menerapkannya sebagai tipe kelas tidak membuat implementasi yang sesuai. Lihat komentar chris.
LF
1
@ LF Apakah standar secara teknis mengharuskan jenis dasar bukan jenis kelas?
eerorika
2
@eerorika: is_classdan is_null_pointerkeduanya tidak mungkin benar untuk tipe yang sama. Hanya satu dari fungsi kategori tipe primer yang dapat mengembalikan true untuk tipe tertentu.
Nicol Bolas

Jawaban:

20

Saya ingin tahu cara kerja nullptr.

Ia bekerja dengan cara yang sesederhana mungkin: dengan fiat . Ini bekerja karena standar C ++ mengatakan itu berfungsi, dan bekerja dengan cara itu karena standar C ++ mengatakan bahwa implementasi harus membuatnya bekerja dengan cara itu.

Sangat penting untuk mengenali bahwa tidak mungkin untuk menerapkan std::nullptr_tmenggunakan aturan bahasa C ++. Konversi dari konstanta null pointer std::nullptr_tke pointer bukan konversi yang ditentukan pengguna. Itu berarti Anda dapat beralih dari konstanta penunjuk nol ke penunjuk, lalu melalui konversi yang ditentukan pengguna ke beberapa jenis lainnya, semuanya dalam satu urutan konversi implisit tunggal.

Itu tidak mungkin jika Anda menerapkan nullptr_tsebagai kelas. Operator konversi mewakili konversi yang ditentukan pengguna, dan aturan urutan konversi implisit C ++ tidak mengizinkan lebih dari satu konversi yang ditentukan pengguna dalam urutan seperti itu.

Jadi kode yang Anda posting adalah perkiraan yang bagus std::nullptr_t, tetapi tidak lebih dari itu. Ini bukan implementasi yang sah dari tipe ini. Ini mungkin dari versi kompiler yang lebih lama (dibiarkan karena alasan kompatibilitas belakang) sebelum kompiler memberikan dukungan yang tepat untuk std::nullptr_t. Anda dapat melihat ini dengan fakta bahwa itu #defines nullptr, sementara C ++ 11 mengatakan itu nullptradalah kata kunci , bukan makro.

C ++ tidak dapat melaksanakan std::nullptr_t, seperti C ++ tidak dapat melaksanakan intatau void*. Hanya implementasinya yang bisa mengimplementasikan hal-hal itu. Inilah yang membuatnya menjadi "tipe fundamental"; itu bagian dari bahasa .


nilainya dapat dikonversi ke tipe integral identik dengan (void *) 0, tetapi tidak mundur;

Tidak ada konversi tersirat dari konstanta penunjuk nol ke tipe integral. Ada konversi dari 0ke tipe integral, tapi itu karena itu adalah nol literer integer, yang merupakan ... integer.

nullptr_tdapat dilemparkan ke tipe integer (via reinterpret_cast), tetapi hanya dapat secara implisit dikonversi ke pointer dan ke bool.

Nicol Bolas
sumber
4
@Wyck: " fiat "
Nicol Bolas
Apa yang dimaksud dengan "tidak mungkin untuk mengimplementasikan std :: nullptr_t menggunakan aturan bahasa C ++"? Apakah ini berarti kompiler C ++ tidak dapat sepenuhnya ditulis dalam C ++ itu sendiri (saya kira tidak)?
northerner
3
@northerner: Maksud saya Anda tidak dapat menulis tipe yang persis sama dengan perilaku yang diperlukan std::nullptr_t. Sama seperti Anda tidak dapat menulis jenis yang persis sama dengan perilaku yang diperlukan int. Anda bisa mendekati, tetapi masih akan ada perbedaan yang signifikan. Dan saya tidak berbicara tentang pendeteksi sifat seperti is_classitu memperlihatkan bahwa tipe Anda ditentukan pengguna. Ada beberapa hal tentang perilaku yang diperlukan dari tipe fundamental yang tidak bisa Anda salin dengan menggunakan aturan bahasa.
Nicol Bolas
1
Hanya berdalih kata-kata. Ketika Anda mengatakan "C ++ tidak dapat mengimplementasikan nullptr_t" Anda berbicara terlalu luas. Dan mengatakan "hanya implementasi yang bisa mengimplementasikannya" hanya membingungkan masalah. Yang Anda maksud adalah itu nullptr_ttidak dapat diimplementasikan " di pustaka C ++ karena itu adalah bagian dari bahasa dasar.
Spencer
1
@Spencer: Tidak, maksud saya persis apa yang saya katakan: C ++ bahasa tidak dapat digunakan untuk mengimplementasikan tipe yang melakukan semua yang std::nullptr_tdiperlukan untuk dilakukan. Sama seperti C ++ bahasa tidak dapat mengimplementasikan tipe yang melakukan semua yang intharus dilakukan.
Nicol Bolas