C++中的nullptr如何实现?

21

我很好奇如何使用 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){};
};

3
nullptr_t 是一种基本类型。int 是如何实现的? - L. F.
10
请注意 #ifdef _LIBCPP_HAS_NO_NULLPTR。这似乎是一个最佳尝试的解决方法,当编译器不提供 nullptr 时使用。 - chris
6
标准规定nullptr_t是一种基本类型。将其实现为类类型不符合规范。请参考Chris的评论。 - L. F.
1
@L.F. 标准在技术上要求基本类型不是类类型吗? - eerorika
2
@eerorika: 对于同一类型,is_classis_null_pointer不能同时为真。对于特定类型,仅有一个基本类型类别函数可以返回true。 - Nicol Bolas
显示剩余11条评论
2个回答

23

我很好奇nullptr的工作原理。

nullptr 是通过简单的人为规定来实现的。 C++标准规定了nullptr的工作方式,编译器必须按照这种方式来实现。

需要注意的是,使用C ++语言的规则无法实现std :: nullptr_t。从类型为std :: nullptr_t的空指针常量到指针的转换不是用户定义的转换。这意味着您可以在单个隐式转换序列中从空指针常量转换为指针,然后通过用户定义的转换转换为其他某些类型。

如果将nullptr_t实现为类,则不可能实现上述操作。转换运算符表示用户定义的转换,而C ++的隐式转换序列规则不允许在此类序列中进行多个用户定义的转换。

你发的代码是 std::nullptr_t 的一个不错的近似,但它只是那样而已。这并不是该类型的合法实现。这可能来自编译器的早期版本(为了向后兼容而保留),在编译器提供对 std::nullptr_t 的正确支持之前。你可以看到它定义了 nullptr ,而 C++11 规定 nullptr 是一个关键字,而不是宏。

C++ 无法实现 std::nullptr_t,就像 C++ 无法实现 intvoid* 一样。只有编译器实现才能实现这些东西。这使它成为“基本类型”;它是语言的一部分。


它的值可以转换为整数类型,与 (void*)0 相同,但不能反向转换;

从空指针常量到整数类型没有隐式转换。存在从 0 到整数类型的转换,但那是因为它是整数字面值零,它是一个整数。

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


“使用C++语言规则无法实现std::nullptr_t是什么意思?”这是否意味着C++编译器不能完全用C++本身编写(我猜不是)? - northerner
3
@northerner的意思是,你无法编写一个完全等同于std::nullptr_t所需行为的类型,就像你无法编写一个完全等同于int所需行为的类型一样。你可以接近,但仍会存在显着差异。而他所说的不是像is_class这样暴露出您的类型是用户定义的特征探测器的问题。有些关于基本类型所需行为的事情是你不能通过使用语言规则来复制的。 - Nicol Bolas
1
只是一点措辞上的争议。当您说“C++无法实现nullptr_t”时,您说得太笼统了。而且说“只有实现可以实现它”只会使事情更加混乱。您的意思是nullptr_t无法在C++库中实现,因为它是基本语言的一部分。 - Spencer
1
@Spencer:不,我的意思就是我所说的:C++语言无法用来实现一个类型,该类型可以完成所有std::nullptr_t所需完成的任务。就像C++语言无法实现一个类型,该类型可以完成所有int所需完成的任务一样。 - Nicol Bolas
@Spencer:从第二段的第一句话可以看出,“无法使用C++语言的规则实现std::nullptr_t”。上下文很重要。 - Nicol Bolas
显示剩余2条评论

0

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


所以你的意思是 nullptr_t 是一种内置类型? - roachsinai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接