6

有点史前史。

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

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

其中一个基类是utils::RefObject,它代表了一个类似于 Windows 的 IUnknown 的概念:它提供了一个引用计数器来确定它的生命周期,以及一个从基类指针查询特定接口的方法。还有template< typename T > utils::Ref,专门为这种对象设计的。它持有一个std::atomic< utils::RefObject* >并在构造、赋值和销毁时自动更新其对象的引用计数,类似于std::shared_ptr. 它还允许通过查询方法隐式转换不同类型的 RefObject。但是,查询一个对象的自身类型效率低下,因此,utils::Ref重载了它的大多数运算符,例如,有特定的utils::Ref< T >::Ref( T* ptr )构造函数,它只是增加传递的对象的引用计数,并且一般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. 虽然,并非无处不在。

快速总结:在标题之前包含 <gext.h> 之前,我nullptr_t没有任何一个std::或编译得很好。using namespace

到目前为止,我一直在使用的网站 cplusplus.com/reference 表明 global::nullptr_t 正是它应该的样子。另一方面,en.cppreference.com wiki告诉它实际上是std::nullptr_t.

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

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

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

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

为什么包含 <gext.h> 会破坏它?=> 如果在 <mutex> 之前包含任何 stddef 标头,则类型根据标准定义,如std::nullptr_t. <glext.h> 包括 <windows.h>,而后者当然也包括 stddef 标头,以及 WinAPI 所需的一整包其他标头。

以下是定义相关类的来源:

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

正如评论中所建议的,我在一个编译的测试用例上运行了 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任何地方时,尽管上面有一点。

4

1 回答 1

5

的类型nullptr是在命名空间中定义的::std,所以正确的限定是::std::nullptr_t。当然,这意味着您通常std::nullptr_t在实践中拼写它。

引用 C++11:

2.14.7/1:

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

18.2/9:

nullptr_t定义如下:

namespace std {
  typedef decltype(nullptr) nullptr_t;
}

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

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

每个 C 标头都有一个名为 form 的名称,其name.h行为就好像每个由相应cname标头放置在标准库命名空间中的名称都放置在全局命名空间范围内一样。

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

于 2015-02-24T13:08:28.927 回答