C++11: Principles of nullptr_t and nullptr

introduction

The C++98 standard defines that the null pointer is NULL, and NULL is a Macro constant. The definition format is:

#ifdef __cplusplus  
#define NULL 0
#else  
#define NULL ((void *)0)  
#endif

The reason for this definition is that the C language allows void* to be implicitly converted to any pointer type, while C++ does not allow this operation, but C++ allows any pointer to be assigned a value of 0.

Nullptr is a newly introduced feature of C++11, an upgraded version of C++98 standard NULL. Although nullptr is a keyword introduced by C++11, unlike the 98 version, nullptr has a type, and its type is nullptr_t. It should be noted that nullptr_t is a standard feature of C++11~C++20, but not a feature of C99~C17 standard. C language only supports nullptr_t from C23.

nullptr

nullptr is a standard rvalue constant. The C++11~C++20 standard stipulates that nullptr has the following characteristics:

  • nullptr can be implicitly converted to a null pointer of any pointer type and any member pointer type. For example:
int * p = nullptr;    // nullptr可以赋值给int*
std::string * p2 = nullptr; // nullptr可以赋值给std::string*
std::nullptr_t&& n = nullptr; // nullptr可以赋值给std::nullptr_t右值引用
std::nullptr_t& nn = nullptr; // 编译报错,不允许nullptr赋值给左值引用
  • Since nullptr is an rvalue, the address operation cannot be performed on nullptr, for example:
std::nullptr_t* p1 = &nullptr; // 编译报错,不允许对nullptr做取地址运算
  • Make a null pointer judgment bool operation on the pointer, and the result of the nullptr operation is false, for example:
int * p = nullptr;    // nullptr可以赋值给int*
if (!p) 
{
    
    
	std::cout << "p空指针运算结果为false..";
}
  • The memory size of nullptr is equivalent to void*, that is to say sizeof(nullptr) === sizeof(void*), for example:
if (sizeof(nullptr) == sizeof(void*))
{
    
    
	std::cout << "sizeof(nullptr) == sizeof(void*)\n";
}
  • For backward compatibility, C++11 still allows NULL to represent a null pointer, so the expression nullptr == NULL is true, but nullptr is not 0. There is also no implicit conversion to an integer.
if (nullptr == NULL)
{
    
    
	std::cout << "nullptr == NULL为true \n";
}

nullptr_t

The nullptr_t data type exists in the header file <stddef.h>, specifically declared:

#ifdef __cplusplus
namespace std
{
    
    
    typedef decltype(nullptr) nullptr_t;
}

using ::std::nullptr_t;
#endif

The C++ standard allows us to define our own nullptr through nullptr_t, and any nullptr_t object and nullptr are true for == operations, and false for != operations. For example:

std::nullptr_t n = std::nullptr_t();
if (n == nullptr)
{
    
    
	std::cout << "n == nullptr is true\n";
}

if (!(n != nullptr))
{
    
    
	std::cout << "n != nullptr is false\n";
}

(n == nullptr) is true
(n != nullptr) is false

Implementation of nullptr_t

Based on the discussion and analysis of nullptr and nullptr_t above, the author concludes the possible realization of nullptr_t:

namespace ptr
{
    
    
struct nullptr_t
{
    
    
	void* __lx;

	inline constexpr nullptr_t() : __lx(0) {
    
    }
	inline constexpr nullptr_t(int) : __lx(0) {
    
    }

	template <class _Tp>
	inline constexpr operator _Tp* () const {
    
     return 0; }

	template <class _Tp, class _Up>
	inline constexpr operator _Tp _Up::* () const {
    
     return 0; }

    nullptr_t* operator&() const = delete;

	friend inline constexpr bool operator==(nullptr_t, nullptr_t) {
    
     return true; }
	friend inline constexpr bool operator!=(nullptr_t, nullptr_t) {
    
     return false; }
};

inline constexpr nullptr_t __get_nullptr_t() {
    
     return nullptr_t(0); }

#define nullptr __get_nullptr_t()
}

According to the characteristics of nullptr and nullptr_t, it can be verified one by one:

int * p = ptr::nullptr;    // nullptr可以赋值给int*
ptr::nullptr_t&& n = ptr::nullptr; // nullptr可以赋值给std::nullptr_t右值引用
ptr::nullptr_t* pp = &ptr::nullptr;  // 编译报错,“ptr::nullptr_t *ptr::nullptr_t::operator &(void) const”: 尝试引用已删除的函数

ptr::nullptr_t nn = ptr::nullptr_t();
if (nn == ptr::nullptr)
{
    
    
	std::cout << "(n == nullptr) is true\n";
}

if (!(nn != ptr::nullptr))
{
    
    
	std::cout << "(n != nullptr) is false\n";
}

if (ptr::nullptr == NULL)
{
    
    
	std::cout << "nullptr == NULL为true \n";
}

Benefits of nullptr

Solve integer 0 and null pointer confusion

Before C++11, NULL was the integer 0, so NULL can represent either the integer 0 or a null pointer. This can sometimes cause us some confusion. For example:

void foo(int) {
    
    }
void foo(int*) {
    
    }

foo(0);     // 调用foo(int)而不是foo(int*)
foo(NULL);  // 如果NULL是0,则调用foo(int);

We expect foo(NULL) to call foo(int*) , but it actually calls foo(int) . And nullptr can solve this problem very well. This is also the motivation for nullptr.

catch null pointer

The type of the null pointer nullptr is nullptr_t, and we can capture the null pointer through the nullptr_t type. For example:

try 
{
    
    
	...
	throw nullptr;
}
catch (nullptr_t)
{
    
    
	...
}

However, if throw NULL, what type do we use to catch? Use int type to catch? Is it a bit awkward.

Summarize

nullptr is a typed rvalue constant, which can solve the problems of integer 0 and null pointer. Therefore, it is recommended that you use nullptr instead of NULL in the development process.

Guess you like

Origin blog.csdn.net/liuguang841118/article/details/127044025