การใช้ c ++ nullptr ทำงานอย่างไร


13

ฉันอยากรู้ว่าnullptrทำงานอย่างไร มาตรฐาน N4659 และ N4849 พูดว่า:

  1. มันจะต้องมีประเภทstd::nullptr_t;
  2. คุณไม่สามารถใช้ที่อยู่ของมัน
  3. มันสามารถแปลงเป็นตัวชี้และตัวชี้ไปยังสมาชิกโดยตรง
  4. sizeof(std::nullptr_t) == sizeof(void*);
  5. การแปลงboolเป็นfalse;
  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เป็นประเภทพื้นฐาน การใช้มันเป็นประเภทคลาสไม่ได้ทำให้การใช้งานที่สอดคล้อง ดูความคิดเห็นของคริส
LF

1
@LF มาตรฐานทางเทคนิคกำหนดให้ประเภทพื้นฐานไม่ใช่ประเภทคลาสหรือไม่?
eerorika

คำตอบ:


20

ฉันอยากรู้ว่า nullptr ทำงานอย่างไร

มันทำงานได้ในวิธีที่ง่ายที่สุดที่เป็นไปได้: โดยfiat มันทำงานได้เพราะมาตรฐาน C ++ บอกว่าใช้งานได้และทำงานตามที่ทำได้เพราะมาตรฐาน C ++ บอกว่าการนำไปปฏิบัติต้องทำให้มันใช้งานได้ในแบบนั้น

สิ่งสำคัญคือต้องตระหนักว่าเป็นไปไม่ได้จะนำไปใช้std::nullptr_tโดยใช้กฎของภาษา C ++ การแปลงจากค่าคงที่ตัวชี้โมฆะของชนิดstd::nullptr_tเป็นตัวชี้ไม่ใช่การแปลงที่ผู้ใช้กำหนด นั่นหมายความว่าคุณสามารถเปลี่ยนจากตัวชี้โมฆะเป็นค่าคงที่ไปยังตัวชี้จากนั้นผ่านการแปลงที่ผู้ใช้กำหนดเป็นประเภทอื่นทั้งหมดในลำดับการแปลงโดยนัยเดียว

ไม่สามารถทำได้หากคุณใช้งานnullptr_tเป็นคลาส โอเปอเรเตอร์การแปลงแสดงถึงการแปลงที่ผู้ใช้กำหนดและกฎลำดับการแปลงโดยนัยของ C ++ ไม่อนุญาตให้มีการแปลงที่ผู้ใช้กำหนดเองมากกว่าหนึ่งรายการในลำดับดังกล่าว

ดังนั้นรหัสที่คุณโพสต์นั้นเป็นการประมาณที่ดีstd::nullptr_tแต่ไม่มีอะไรมากไปกว่านั้น มันไม่ได้เป็นประเภทที่ถูกต้องตามกฎหมาย นี่อาจจะเป็นจากรุ่นเก่าของคอมไพเลอร์ (ที่เหลืออยู่ในสำหรับเหตุผลที่เข้ากันได้ย้อนหลัง) std::nullptr_tก่อนที่จะคอมไพเลอร์ที่ให้การสนับสนุนที่เหมาะสมสำหรับ คุณสามารถดูนี้ด้วยความจริงที่ว่ามัน#defines nullptrในขณะที่ C ++ 11 บอกว่าnullptrเป็นคำหลักไม่มาโคร

C ++ ไม่สามารถดำเนินการstd::nullptr_tเช่นเดียวกับ C ++ ไม่สามารถดำเนินการหรือint void*มีเพียงการดำเนินการเท่านั้นที่สามารถนำสิ่งเหล่านั้นมาใช้ นี่คือสิ่งที่ทำให้มันเป็น "ประเภทพื้นฐาน"; มันเป็นส่วนหนึ่งของภาษา


ค่าของมันสามารถแปลงเป็นชนิดอินทิกรัลเป็น (void *) 0 ได้ แต่ไม่ย้อนกลับ

นอกจากนี้ไม่มีการแปลงโดยปริยายจากคงชี้โมฆะประเภทหนึ่ง มีการแปลงจาก0เป็นประเภทที่สมบูรณ์ แต่นั่นเป็นเพราะมันเป็นศูนย์ตัวอักษรจำนวนเต็มซึ่งก็คือ ... จำนวนเต็ม

nullptr_tสามารถโยนไปยังชนิดจำนวนเต็ม (ผ่านreinterpret_cast) boolแต่ก็สามารถแปลงไปโดยปริยายชี้และ


4
@Wyck: " fiat "
Nicol Bolas

สิ่งที่มีความหมายโดย "มันเป็นไปไม่ได้ที่จะใช้ std :: nullptr_t โดยใช้กฎของภาษา C ++"? หมายความว่าคอมไพเลอร์ C ++ ไม่สามารถเขียนด้วยตัวเองได้อย่างสมบูรณ์ใน C ++ (ฉันเดาว่าไม่ได้)?
เหนือ

3
@northerner: std::nullptr_tฉันหมายความว่าคุณไม่สามารถเขียนประเภทที่ว่าเทียบเท่ากับพฤติกรรมที่จำเป็นของ intเพียงเท่านี้คุณไม่สามารถเขียนประเภทที่ว่าเทียบเท่ากับพฤติกรรมที่จำเป็นของ คุณสามารถเข้าใกล้ แต่จะยังคงมีความแตกต่างที่สำคัญ และฉันไม่ได้พูดถึงเครื่องตรวจจับคุณสมบัติเช่นis_classนั้นเปิดเผยว่าประเภทของคุณเป็นแบบที่ผู้ใช้กำหนด มีบางอย่างเกี่ยวกับพฤติกรรมที่ต้องการของประเภทพื้นฐานที่คุณไม่สามารถคัดลอกได้โดยใช้กฎของภาษา
Nicol Bolas

1
เพียงแค่การเล่นโวหารถ้อยคำ เมื่อคุณพูดว่า "C ++ ไม่สามารถใช้งานได้nullptr_t" คุณพูดได้กว้างเกินไป และการพูดว่า "การติดตั้งใช้งานได้เพียงอย่างเดียวเท่านั้น" ทำให้เกิดความสับสน สิ่งที่คุณหมายถึงคือnullptr_tไม่สามารถใช้งานได้ " ในไลบรารี C ++เพราะเป็นส่วนหนึ่งของภาษาฐาน
Spencer

1
@ สเปนเซอร์: ไม่ฉันหมายถึงสิ่งที่ฉันพูดอย่างแน่นอน: C ++ ภาษานั้นไม่สามารถใช้ในการสร้างประเภทที่ทำทุกอย่างที่std::nullptr_tจำเป็นต้องทำ ภาษา C ++ ไม่สามารถใช้ประเภทที่ทำทุกอย่างที่intจำเป็นต้องทำ
Nicol Bolas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.