C++ nullptr 实现如何工作?

How does c++ nullptr implementation work?

我很想知道 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){};
};

I am curious to know how nullptr works.

它以最简单的方式工作:通过法定。它之所以有效,是因为 C++ 标准 它有效,并且它以它的方式工作,因为 C++ 标准说实现必须 使其工作 时尚。

认识到使用 C++ 语言的规则 不可能 实现 std::nullptr_t 是很重要的。从 std::nullptr_t 类型的空指针常量到指针的转换不是用户定义的转换。这意味着您可以从空指针常量到指针,然后通过用户定义的转换到其他类型,所有这些都在一个隐式转换序列中。

如果将 nullptr_t 实现为 class,这是不可能的。转换运算符代表用户定义的转换,C++的隐式转换序列规则不允许在这样的序列中有超过一个用户定义的转换。

所以您发布的代码是 std::nullptr_t 的一个很好的近似值,但仅此而已。它不是该类型的合法实现。这可能来自编译器为 std::nullptr_t 提供适当支持之前的旧版本编译器(出于向后兼容性原因保留)。您可以通过它 #defines nullptr 的事实看到这一点,而 C++11 说 nullptr 关键字 ,而不是宏.

C++ 无法实现 std::nullptr_t,就像 C++ 无法实现 intvoid* 一样。只有实现才能实现那些东西。这就是它成为 "fundamental type" 的原因;它是语言.

的一部分

its value can be converted to integral type identically to (void*)0, but not backwards;

从空指针常量到整数类型有 no implicit conversion。有一个从 0 到整数类型的转换,但那是因为它是整数字面值零,它是……一个整数。

nullptr_t 可以 cast 为整数类型(通过 reinterpret_cast),但它只能隐式转换为指针和 bool.

nullptr 是一种基本类型,它是 C++ 语言实现的一部分。与 NULL 关键字不同,NULL 关键字是在整数常量 0 中计算的宏,nullptr 实际上是一个隐含指针文字的关键字。由于 int 实际上是 4 字节 (x64) 语言的内置类型,因此 nullptr 也是 8 字节 (x64)

的内置类型