nullptr_t驻留在哪里?
一些史前史.
我已经写了很长时间的游戏引擎.它分为几个静态库,例如"utils","rsbin"(资源系统),"window",然后链接到一个可执行文件中.
I've been writing a game engine for quite some time. It's divided into several static libraries, like "utils", "rsbin" (resource system), "window", which are then linked into a single executable.
这是一个跨平台引擎,正在针对Windows和Android进行编译. 在Windows下,我使用MinGW进行编译.在Android下,使用CCTools,它是本机gcc的界面.
It is a crossplatform engine, being compiled for Windows and for Android. Under Windows, I compile it with MinGW. Under Android, with CCTools, which is an interface to native gcc.
基类之一是utils::RefObject
,它表示类似于Windows的IUnknown的概念:它提供了一个确定其生存期的引用计数器,以及一种用于从基类指针中查询特定接口的方法.还有template< typename T > utils::Ref
,专门为此类对象设计的.它拥有一个std::atomic< utils::RefObject* >
,并在构造,分配和销毁后自动更新其对象的引用计数,类似于std::shared_ptr
.它还允许通过其查询方法隐式转换不同类型的RefObject.虽然,查询对象的自身类型效率不高,所以utils::Ref
重载了其大多数运算符,例如e. G.有一个特定的utils::Ref< T >::Ref( T* ptr )
构造函数,该构造函数仅增加传递的对象的refcount,而一个普通的utils::Ref< T >::Ref( RefObject* ptr )
,该查询为T实例查询其参数,并在失败时引发异常(不过,请放心,当然有一种方法可以强制转换).
One of the base classes is utils::RefObject
, which represents a concept similar to Windows's IUnknown: it provides a reference counter to determine its lifetime and a method for querying a specific interface from base class pointer. There's also template< typename T > utils::Ref
, designed specifically for this kind of objects. It holds an std::atomic< utils::RefObject* >
and automatically updates its object's refcount upon construction, assignment and destruction, analogously to std::shared_ptr
. It also allows to implicitly convert RefObjects of different types through their querying methods. Though, it's inefficient to query an object for its own type, so, utils::Ref
overloads most of its operators, e. g. there's specific utils::Ref< T >::Ref( T* ptr )
constructor, which simply increments the passed object's refcount, and general utils::Ref< T >::Ref( RefObject* ptr )
, which queries its argument for instance of T and throws an exception on failure (don't worry, though, there's of course a method for the soft cast).
但是只有这两种方法会带来一个问题:由于模棱两可,您无法使用空指针显式初始化utils::Ref
;因此,还有utils::Ref< T >::Ref( nullptr_t )
提供了一种实现方法.
But having just these two methods introduces a problem: you cannot explicitly initialize utils::Ref
with a null pointer, as it is ambiguous; so there's also utils::Ref< T >::Ref( nullptr_t )
to provide a way to do it.
现在,我们正在解决手头的问题.在头文件中,原型的拼写与上面的完全相同,没有任何前面的std::
.请注意,我也不使用using namespace
.长期以来,这一直有效.
Now, we are getting to the problem at hand. In the header file, the prototype is spelled exactly as above, without any preceding std::
. Note that I don't use using namespace
either. For a long time, this worked.
现在,我正在研究图形系统.它以前存在过,但还很初级,所以我什至没有注意到< gl.h>实际上仅定义了OpenGL 1.1,而对于较新的版本,则应浏览< glext.h>.现在,有必要使用后者.但包括它破坏了旧的参考类.
Now, I'm working on a graphics system. It existed before, but it was rather rudimentary, so I didn't even noticed that <gl.h> actually defines only OpenGL 1.1, while for newer versions you should go through <glext.h>. Now, it became necessary to use the latter. But including it broke the old reference class.
从错误消息来看,MinGW现在在原型中的nullptr_t
存在问题.我在网上进行了快速搜索,发现经常被称为std::nullptr_t
.不过,并非到处都是.
Judging from error messages, MinGW now has problems with that nullptr_t
in prototypes. I've done a quick search on the Web and found that often it's referred to as std::nullptr_t
. Though, not everywhere.
快速总结:在没有std::
或using namespace
的情况下,我可以使用nullptr_t
进行编译,直到我将< glext.h>包括在内.在标题之前.
Quick sumup: I had nullptr_t
without either std::
or using namespace
compiling fine until I included <glext.h> before the header.
到目前为止,我一直在使用的网站cplusplus.com/reference表示全局::nullptr_t
告诉我们实际上是
The site I've been using so far, cplusplus.com/reference, suggests that global ::nullptr_t
is exactly how it should be. On the other hand, en.cppreference.com wiki tells that it's actually std::nullptr_t
.
一个快速测试程序,一个具有void foo( int )
和void foo( nullptr_t )
的helloworld,未能编译,并且现在的原因是明确的"error: 'nullptr_t' was not declared in this scope"
并建议使用std::nullptr_t
.
A quick test program, a helloworld with void foo( int )
and void foo( nullptr_t )
, failed to compile and the reason now is explicit "error: 'nullptr_t' was not declared in this scope"
with suggestion to use std::nullptr_t
instead.
在需要的地方添加std::
并不困难;但是这种情况让我颇为好奇.
It won't be hard to add std::
where needed; but this case left me rather curious.
cplusplus.com实际上是在说谎吗? =>以逗号回答,是的.来源不正确.
cplusplus.com was actually lying? => Answered in commets, yes. It's an inaccurate source.
然后,如果nullptr_t
实际上驻留在namespace std
中,为什么utils::Ref
可以编译? =>通过注释中的建议,进行了几次测试,发现包含在其他某些标头中的< mutex>当放置在任何stddef标头之前时,定义了全局::nullptr_t
.当然不是理想的行为,但这不是主要的错误.无论如何,应该将其报告给MinGW/GCC开发人员.
Then, if nullptr_t
actually resides in namespace std
, why did utils::Ref
compile? => With suggestions in comments, ran a couple of test and discovered that <mutex>, included in some other header, when placed before any stddef header, defines global ::nullptr_t
. Certainly not an ideal behavior, but it's not a major bug. Probably should report it to MinGW/GCC developers anyway.
为什么要包含< glext.h>坏了吗? =>如果在< mutex>之前包含任何stddef标头,则根据标准将类型定义为std::nullptr_t
. < glext.h>包括< windows.h> ;,它当然也包括stddef标头,以及WinAPI所需的其他完整包.
Why inclusion of <glext.h> breaks it? => When any stddef header is included before <mutex>, the type is defined according to the standard, as std::nullptr_t
. <glext.h> includes <windows.h>, which, in turn, certainly includes the stddef header, among with a whole pack of others which are needed for WinAPI.
以下是定义所涉及类的来源:
Here are the sources, defining the class in question:
- 实用程序/ref.hpp
- 实用程序/ref.cpp
- utils/refobject.hpp
- utils/refobject.cpp
- utils/logger.hpp =>该程序使用互斥量以避免在输出过程中出现行撕裂.
- utils/cbase.hpp
- utils/ref.hpp
- utils/ref.cpp
- utils/refobject.hpp
- utils/refobject.cpp
- utils/logger.hpp => This one uses a mutex to avoid line tearing during output.
- utils/cbase.hpp
(包括后2个,因此可能也会影响)
(the latter 2 are included and so may affect too)
正如评论中建议的那样,我在编译的测试用例上运行g ++ -E,并在< stddef.h>中找到了一个非常有趣的地方:
As suggested in the comments, I ran g++ -E on a test case which compiled, and found a quite interesting bit in <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之外什么都找不到.
Now to find where _GXX_NULLPTR_T
is defined else... a quick GREP through MinGW's files didn't find anything besides this stddef.h
因此,为什么以及如何使其禁用仍然是一个谜.特别是当仅包括< stddef.h>时,除了上述内容,没有其他地方在任何地方都没有定义nullptr_t
.
So, it's still a mystery why and how it's getting disabled. Especially when including just <stddef.h> and nothing else does not define nullptr_t
anywhere, despite the bit above.
nullptr
的类型是在名称空间::std
中定义的,因此正确的限定条件是::std::nullptr_t
.当然,这意味着您通常在实践中将其拼写为std::nullptr_t
.
The type of nullptr
is defined in namespace ::std
, so the correct qualification is ::std::nullptr_t
. Of course, this means you normally spell it std::nullptr_t
in practice.
引用C ++ 11:
2.14.7/1:
指针文字是关键字
nullptr
.这是std::nullptr_t
类型的prvalue.
The pointer literal is the keyword
nullptr
. It is a prvalue of typestd::nullptr_t
.
18.2/9:
nullptr_t
的定义如下:
namespace std {
typedef decltype(nullptr) nullptr_t;
}
nullptr_t
是同义词的类型具有在3.9.1和4.10中描述的特征. [注意:
尽管无法获取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>
,因此这是定义了std::nullptr_t
的C ++标头.根据D.5/2:
<stddef.h>
also enters into the picture. 18.2 talks about <cstddef>
, so that's the C++ header where std::nullptr_t
is defined. Per D.5/2:
每个C头文件,每个文件头的名称格式均为
name.h
,其行为就像在标准中放置的每个名称一样. 相应的cname
标头中的库名称空间放置在全局名称空间范围内.
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 correspondingcname
header is placed within the global namespace scope.
这意味着包含<stddef.h>
使您可以访问::nullptr_t
.但是,由于这应该是C头文件,所以我建议不要在C ++代码中依赖它(即使它在形式上是有效的).
Which means that including <stddef.h>
gives you access to ::nullptr_t
. But since that's supposed to be a C header, I would advise against relying on this in C++ code (even if it's formally valid).