c++ nullptr 实现是如何工作的?

Ful*_*ngo 19 c++ null-pointer nullptr c++17

我很想知道它是如何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
Run Code Online (Sandbox Code Playgroud)

不过我对第一部分更感兴趣。它似乎满足第 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){};
};
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 22

我很想知道 nullptr 是如何工作的。

它以最简单的方式工作:通过fiat。它有效是因为 C++ 标准说它有效,它以它的方式工作是因为 C++ 标准说实现必须使它以这种方式工作

重要的是要认识到使用 C++ 语言的规则是不可能实现std::nullptr_t的。从类型的空指针常量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

  • @northerner:我的意思是您无法编写与“std::nullptr_t”所需行为完全相同的类型。正如您无法编写与“int”所需行为完全相同的类型。你可以接近,但仍然会有显着的差异。我不是在谈论像“is_class”这样的特征检测器,它会暴露您的类型是用户定义的。有些关于基本类型所需行为的事情是您无法通过使用语言规则来复制的。 (3认同)