现在c++ 11有了许多新特性。一个有趣而令人困惑的(至少对我来说)是新的nullptr。
不需要讨厌的宏NULL了。
int* x = nullptr;
myclass* obj = nullptr;
不过,我还是不明白nullptr是如何工作的。例如,维基百科的一篇文章说:
c++ 11通过引入一个新的关键字作为区分空指针常量nullptr来纠正这一点。它的类型为nullptr_t,可隐式转换,可与任何指针类型或指针到成员类型相比较。它不能隐式转换,也不能与整型相比,bool类型除外。
它如何既是关键字又是类型的实例?
此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?
让我首先给您一个简单的nullptr_t的实现
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
nullptr是返回类型解析器习惯用法的一个微妙示例,它根据分配给实例的类型自动推导出正确类型的空指针。
int *ptr = nullptr; // OK
void (C::*method_ptr)() = nullptr; // OK
如上所述,当nullptr被赋值给整数指针时,将创建模板化转换函数的int类型实例化。方法指针也是一样。
通过这种方式利用模板功能,我们实际上每次都创建了适当类型的空指针,这是一个新的类型赋值。
因为nullptr是一个值为0的整型字面值,你不能使用它的地址,这是我们通过删除&操作符实现的。
为什么我们首先需要nullptr ?
你可以看到传统的NULL有一些问题,如下:
1️⃣隐式转换
char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
2️⃣函数调用歧义
void func(int) {}
void func(int*){}
void func(bool){}
func(NULL); // Which one to call?
编译会产生以下错误:
error: call to 'func' is ambiguous
func(NULL);
^~~~
note: candidate function void func(bool){}
^
note: candidate function void func(int*){}
^
note: candidate function void func(int){}
^
1 error generated.
compiler exit status 1
3️⃣构造函数重载
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
在这种情况下,需要显式强制转换(即String s((char*)0))。
让我首先给您一个简单的nullptr_t的实现
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
nullptr是返回类型解析器习惯用法的一个微妙示例,它根据分配给实例的类型自动推导出正确类型的空指针。
int *ptr = nullptr; // OK
void (C::*method_ptr)() = nullptr; // OK
如上所述,当nullptr被赋值给整数指针时,将创建模板化转换函数的int类型实例化。方法指针也是一样。
通过这种方式利用模板功能,我们实际上每次都创建了适当类型的空指针,这是一个新的类型赋值。
因为nullptr是一个值为0的整型字面值,你不能使用它的地址,这是我们通过删除&操作符实现的。
为什么我们首先需要nullptr ?
你可以看到传统的NULL有一些问题,如下:
1️⃣隐式转换
char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
2️⃣函数调用歧义
void func(int) {}
void func(int*){}
void func(bool){}
func(NULL); // Which one to call?
编译会产生以下错误:
error: call to 'func' is ambiguous
func(NULL);
^~~~
note: candidate function void func(bool){}
^
note: candidate function void func(int*){}
^
note: candidate function void func(int){}
^
1 error generated.
compiler exit status 1
3️⃣构造函数重载
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
在这种情况下,需要显式强制转换(即String s((char*)0))。
这是LLVM头文件。
// -*- C++ -*-
//===--------------------------- __nullptr --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP_NULLPTR
#define _LIBCPP_NULLPTR
#include <__config>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
#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
#endif // _LIBCPP_NULLPTR
(使用grep -r /usr/include/* '可以发现很多内容)
一个突出的东西是操作符* overload(返回0比分段故障友好得多……)
另一件事是,它看起来与存储地址根本不兼容。与抛出void*和将NULL结果作为哨兵值传递给普通指针的方式相比,这显然会减少“永远不要忘记,它可能是一个炸弹”的因素。