Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does c++ nullptr implementation work?

I am curious to know how nullptr works. Standards N4659 and N4849 say:

  1. it has to have type std::nullptr_t;
  2. you cannot take its address;
  3. it can be directly converted to a pointer and pointer to member;
  4. sizeof(std::nullptr_t) == sizeof(void*);
  5. its conversion to bool is false;
  6. its value can be converted to integral type identically to (void*)0, but not backwards;

So it is basically a constant with the same meaning as (void*)0, but it has a different type. I have found the implementation of std::nullptr_t on my device and it is as follows.

#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

I am more interested in the first part though. It seems to satisfy the points 1-5, but I have no idea why it has a subclass __nat and everything related to it. I would also like to know why it fails on integral conversions.

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){};
};
like image 466
Fullfungo Avatar asked Apr 18 '20 02:04

Fullfungo


People also ask

Does nullptr work in C?

nullptr is a new keyword introduced in C++11. nullptr is meant as a replacement to NULL . nullptr provides a typesafe pointer value representing an empty (null) pointer. The general rule of thumb that I recommend is that you should start using nullptr whenever you would have used NULL in the past.

What does nullptr do in C++?

The nullptr keyword represents a null pointer value. Use a null pointer value to indicate that an object handle, interior pointer, or native pointer type does not point to an object. Use nullptr with either managed or native code.

What is nullptr in C++11?

C++11 corrects this by introducing a new keyword to serve as a distinguished null pointer constant: nullptr. It is of type nullptr_t, which is implicitly convertible and comparable to any pointer type or pointer-to-member type. It is not implicitly convertible or comparable to integral types, except for bool.

Is nullptr same as null C++?

Nullptr vs NULLNULL is 0 (zero) i.e. integer constant zero with C-style typecast to void* , while nullptr is prvalue of type nullptr_t , which is an integer literal that evaluates to zero.


1 Answers

I am curious to know how nullptr works.

It works in the simplest way possible: by fiat. It works because the C++ standard says it works, and it works the way it does because the C++ standard says that implementations must make it work in that fashion.

It's important to recognize that it is impossible to implement std::nullptr_t using the rules of the C++ language. The conversion from a null pointer constant of type std::nullptr_t to a pointer is not a user-defined conversion. That means that you can go from a null pointer constant to a pointer, then through a user-defined conversion to some other type, all in a single implicit conversion sequence.

That's not possible if you implement nullptr_t as a class. Conversion operators represent user-defined conversions, and C++'s implicit conversion sequence rules don't allow for more than one user-defined conversion in such a sequence.

So the code you posted is a nice approximation of std::nullptr_t, but it is nothing more than that. It is not a legitimate implementation of the type. This was probably from an older version of the compiler (left in for backwards-compatibility reasons) before the compiler provided proper support for std::nullptr_t. You can see this by the fact that it #defines nullptr, while C++11 says that nullptr is a keyword, not a macro.

C++ cannot implement std::nullptr_t, just as C++ cannot implement int or void*. Only the implementation can implement those things. This is what makes it a "fundamental type"; it's a part of the language.


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

There is no implicit conversion from a null pointer constant to integral types. There is a conversion from 0 to an integral type, but that's because it's the integer literal zero, which is... an integer.

nullptr_t can be cast to an integer type (via reinterpret_cast), but it can only be implicitly converted to pointers and to bool.

like image 59
Nicol Bolas Avatar answered Oct 22 '22 18:10

Nicol Bolas