C ++ nullptr实现如何工作?


13

我很好奇如何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

我对第一部分更感兴趣。它似乎满足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

2
@eerorika:is_classis_null_pointer不能同时为同一类型是真实的。对于特定类型,只有一个主要类型类别函数可以返回true。
Nicol Bolas

Answers:


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:“ 法令
Nicol Bolas

“无法使用C ++语言规则实现std :: nullptr_t”是什么意思?这是否意味着C ++编译器不能完全用C ++本身编写(我想不是)?
北方人

3
@northerner:我的意思是您不能编写与要求的行为完全等效的类型std::nullptr_t。正如您无法编写与要求的行为完全等同的类型一样int。您可以接近,但是仍然会有很大的差异。而且我并不是在谈论这样的特征检测器is_class,因为它表明您的类型是用户定义的。关于基本类型的必需行为,有些事情根本无法通过使用语言规则来复制。
Nicol Bolas

1
只是一个措辞上的小问题。当您说“ C ++无法实现nullptr_t”时,您讲得太广泛了。说“只有实现才能实现”,只会使事情变得混乱。你是什么意思的是,nullptr_t无法实现” 在C ++库,因为它的一部分的基本语言的。
斯宾塞

1
@Spencer:不,我的意思与我所说的完全一样:C ++语言无法用于实现可以执行std::nullptr_t所需所有操作的类型。就像C ++一样,该语言也无法实现一种可以执行int所需所有操作的类型。
Nicol Bolas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.