nullptr_t住在哪里?

Where does nullptr_t reside?

有点史前史。

我编写游戏引擎已经有一段时间了。它分为几个静态库,如 "utils"、"rsbin"(资源系统)、"window",然后链接到一个可执行文件中。

它是一个跨平台引擎,正在为 Windows 和 Android 编译。 在Windows下,我是用MinGW编译的。在 Android 下,使用 CCTools,它是原生 gcc 的接口。

其中一个基础classes是utils::RefObject,它代表了一个类似于Windows的IUnknown的概念:它提供了一个引用计数器来确定它的生命周期和一个方法从基 class 指针查询特定接口。还有专门为这种object设计的template< typename T > utils::Ref。它持有一个 std::atomic< utils::RefObject* > 并在构造、分配和销毁时自动更新其 object 的引用计数,类似于 std::shared_ptr。它还允许通过查询方法隐式转换不同类型的 RefObjects。但是,查询 object 自身类型的效率很低,因此 utils::Ref 重载了它的大部分运算符,例如。 G。有特定的 utils::Ref< T >::Ref( T* ptr ) 构造函数,它简单地增加传递的 object 的引用计数,和通用的 utils::Ref< T >::Ref( RefObject* ptr ),它查询它的参数以获取 T 的实例并在失败时抛出异常(不用担心,虽然,当然有一个软演员的方法)。

但是只有这两种方法会引入一个问题:您不能使用空指针显式初始化 utils::Ref,因为它是不明确的;所以还有 utils::Ref< T >::Ref( nullptr_t ) 提供了一种方法。

现在,我们开始解决手头的问题。在 header 文件中,原型的拼写与上面完全相同,前面没有任何 std::。请注意,我也不使用 using namespace。很长一段时间以来,这都奏效了。

现在,我正在研究图形系统。它以前就存在,但它相当简陋,所以我什至没有注意到 实际上只定义了 OpenGL 1.1,而对于较新的版本,你应该通过 。现在,有必要使用后者。但包括它打破了旧参考 class.

从错误消息来看,MinGW 现在在原型中存在 nullptr_t 的问题。我在网上快速搜索了一下,发现 通常 它被称为 std::nullptr_t。虽然,不是到处都是。

快速总结:我有 nullptr_t 而没有 std::using namespace 编译正常,直到我在 [=130 之前包含 =].

到目前为止我一直在使用的站点cplusplus.com/reference,建议global ::nullptr_t is exactly how it should be. On the other hand, en.cppreference.com wiki tells that 实际上是std::nullptr_t.

一个快速测试程序,一个带有 void foo( int )void foo( nullptr_t ) 的 helloworld,编译失败,现在的原因很明确 "error: 'nullptr_t' was not declared in this scope",建议改用 std::nullptr_t

在需要的地方添加std::并不难;但是这个案例让我很好奇。

cplusplus.com居然在撒谎? => 在评论中回答,是的。这是一个不准确的来源。

那么,如果nullptr_t实际上驻留在namespace std,为什么utils::Ref编译呢? => 根据评论中的建议,运行 一些测试发现 包含在其他一些 header 中,当放置在任何 stddef header 之前时,定义了全局 ::nullptr_t.当然不是理想的行为,但这不是主要错误。无论如何,可能应该将其报告给 MinGW/GCC 开发人员。

为什么包含 会破坏它? => 当在 之前包含任何 stddef header 时,类型根据标准定义为 std::nullptr_t 包括 ,后者当然包括 stddef header,以及 WinAPI 所需的一整套其他内容。

这里是来源,定义了有问题的 class:

(包括后两个,因此也可能影响)

正如评论中所建议的,我 运行 g++ -E 在编译的测试用例上,并在 :

中发现了一个非常有趣的位
#if defined(__cplusplus) && __cplusplus >= 201103L
#ifndef _GXX_NULLPTR_T
#define _GXX_NULLPTR_T
  typedef decltype(nullptr) nullptr_t;
#endif
#endif /* C++11.  */

现在要查找 _GXX_NULLPTR_T 的定义位置...通过 MinGW 的文件快速 GREP 没有找到除此 stddef.h

之外的任何内容

因此,为什么以及如何禁用它仍然是个谜。尤其是当只包含 并且没有其他任何东西都没有定义 nullptr_t 时,尽管上面有一点。

nullptr 的类型在命名空间 ::std 中定义,因此正确的限定是 ::std::nullptr_t。当然,这意味着您通常会在练习中拼写 std::nullptr_t

引用 C++11:

2.14.7/1:

The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t.

18.2/9:

nullptr_t is defined as follows:

namespace std {
  typedef decltype(nullptr) nullptr_t;
}

The type for which nullptr_t is a synonym has the characteristics described in 3.9.1 and 4.10. [ Note: Although nullptr’s address cannot be taken, the address of another nullptr_t object that is an lvalue can be taken. —end note ]

<stddef.h>也进入画面。 18.2讲的是<cstddef>,所以就是C++header里面定义了std::nullptr_t。根据 D.5/2:

Every C header, each of which has a name of the form name.h, behaves as if each name placed in the standard library namespace by the corresponding cname header is placed within the global namespace scope.

这意味着包含 <stddef.h> 可以让您访问 ::nullptr_t。但是因为那应该是 C header,我建议不要在 C++ 代码中依赖它(即使它在形式上是有效的)。