nullptr_t驻留在哪里
Where does nullptr_t reside?
一点史前史。
我写游戏引擎已经有一段时间了。它分为几个静态库,如"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++代码中依赖它(即使它在形式上是有效的)。
- C++我需要了解在哪里使用指针和双指针
- 未定义的引用在哪里
- 谷歌测试中的期望值存储在哪里
- 尽管遵循了规则,内存泄漏在哪里
- 静态数据成员模板专用化的实例化点在哪里
- 在哪里放置我的函数?进入我的母语 Gui 还是进入我的演示者?
- 在哪里声明结构运算符重载
- C++ 中的自定义异常:在哪里定义它们?
- 常量参数存储在哪里 (C++)?
- 如何在 c++ 中确定一条指令(以字节为单位)在哪里结束,另一条指令从哪里开始?
- 此递归函数的每次迭代的值存储在哪里?
- 如何告诉本机节点模块所需的dll存储在哪里?
- 在哪里存储跨平台C++应用存储?
- C++泛型类错误,问题出在哪里?
- 在标准中,模板参数的语法在哪里定义,例如,'std::function<int(char)>'?
- 将类作为主要参数的语法在哪里需要?
- 模板参数在 C++ 中存储在哪里?
- 我的重复检查代码中的错误究竟在哪里?
- 在哪里可以找到 std::bitset 的数据成员?
- 具体来说,标准在哪里规定修改 const 对象是未定义的行为?