nullptr_t驻留在哪里

Where does nullptr_t reside?

本文关键字:在哪里 nullptr      更新时间:2023-10-16

一点史前史。

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

它是一个跨平台的引擎,正在为Windows和Android编译。在Windows下,我用MinGW编译它。在安卓系统下,使用CCTools,这是一个到本地gcc的接口。

其中一个基类是utils::RefObject,它代表了一个类似于Windows的IUnknown的概念:它提供了一个确定其生存期的引用计数器,以及一个从基类指针查询特定接口的方法。还有template< typename T > utils::Ref,它是专门为这类对象设计的。它保存一个std::atomic< utils::RefObject* >,并在构造、分配和销毁时自动更新其对象的refcount,类似于std::shared_ptr。它还允许通过不同类型的RefObject的查询方法隐式转换它们。尽管如此,查询对象的类型是低效的,因此utils::Ref重载了它的大多数运算符,例如,有一个特定的utils::Ref< T >::Ref( T* ptr )构造函数,它只增加传递的对象的refcount,还有一个通用的utils::Ref< T >::Ref( RefObject* ptr ),它查询它的参数,例如T,并在失败时抛出异常(不过别担心,当然有一个软强制转换的方法)。

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

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

现在,我正在开发一个图形系统。它以前存在过,但相当初级,所以我甚至没有注意到<gl.h>实际上只定义了OpenGL 1.1,而对于较新的版本,您应该通过<glext.h>。现在,有必要使用后者。但包括它打破了旧的引用类。

从错误消息来看,MinGW现在在原型中存在nullptr_t问题。我在网上快速搜索了一下,发现通常被称为std::nullptr_t。不过,不是所有地方都有。

快速汇总:我有nullptr_t,没有std::using namespace编译良好,直到我包含<glext.h>在页眉之前

到目前为止,我一直在使用的网站cplusplus.com/reference表明,全局::nullptr_t正是它应该有的样子。另一方面,en.cppreference.com维基告诉它实际上是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要编译?=>在评论中提出建议后,进行了几次测试,发现<互斥锁>,包含在某些其他标头中,当放置在任何stddef标头之前时,定义全局::nullptr_t。当然这不是一个理想的行为,但也不是一个主要的bug。可能无论如何都应该向MinGW/GCC开发人员报告。

为什么包含<glext.h>打破它?=>当在<互斥锁>,根据标准将该类型定义为std::nullptr_t<glext.h>包括<windows.h>,反过来,它当然包括stddef标头,以及WinAPI所需的一整套其他标头。

以下是定义有问题的类的来源:

  • utils/ref.hpp
  • utils/ref.cpp
  • utils/refobject.hpp
  • utils/refobject.cpp
  • utils/logger.hpp=>这一个使用互斥来避免在输出过程中撕裂行
  • utils/cbase.hpp

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

正如评论中所建议的,我在一个编译的测试用例上运行了g++-E,并在<stddef.h>:

#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 之外的任何东西

所以,它为什么以及如何变得残疾仍然是个谜。尤其是当仅包括<stddef.h>并且没有任何其它不在任何地方定义nullptr_t

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

引用C++11:

2.14.7/1:

指针文字是关键字nullptr。它是一个类型为std::nullptr_t的prvalue。

18.2/9:

nullptr_t定义如下:

namespace std {
  typedef decltype(nullptr) nullptr_t;
}

nullptr_t是同义词的类型具有3.9.1和4.10中描述的特征。[注意:虽然nullptr的地址不能取,但作为左值的另一个nullptr_t对象的地址可以被带走--尾注]

CCD_ 41也进入画面。18.2谈到了<cstddef>,所以这是定义std::nullptr_t的C++头。根据D.5/2:

每个C头,每个头都有一个形式为name.h的名称,其行为就好像每个名称都放在标准中一样由相应的CCD_ 45报头表示的库名称空间被放置在全局名称空间范围内。

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