【问题标题】:Is nullptr_t a default constructible type?nullptr_t 是默认的可构造类型吗?
【发布时间】:2012-04-14 17:37:35
【问题描述】:

我无法从 C++11 标准中判断 nullptr_t 是否具有默认构造函数。换句话说,以下是否有效?:

nullptr_t n; 

GCC 和 VC++ 允许使用上述代码,但 clang 不允许。我在标准中找不到任何指定它没有默认构造函数的东西,而我能找到的表明它应该有。这对我很重要,因为我正在编写 nullptr 的基本回退实现以支持旧编译器,并且需要知道是否需要为其提供默认构造函数。

【问题讨论】:

  • 我认为您应该将 nullptr_t 作为普通指针类型处理,即不作为类处理。所以我假设nullptr_t n; 创建了一个 uninitialised 变量;你应该明确写nullptr_t n = nullptr;。但是我这里没有 C++11 编译器,所以无法检查。而且我似乎无法在正式规范中找到我在哪里阅读它......
  • FWIW,clang 接受“nullptr_t n;”在这里。

标签: c++ gcc clang nullptr


【解决方案1】:

标准的内容

标准说 (18.2)

nullptr_t 定义如下:

namespace std {
   typedef decltype(nullptr) nullptr_t;
}

nullptr_t 是同义词的类型具有 3.9.1 中描述的特征和 4.10.

3.9.1 基本上说它应该和void* 的大小相同,而 4.10 则指定了nullptr 的转换规则。

编辑: 3.9.9 进一步明确指出 nullptr_t 是标量类型,这意味着适用于 8.5 中内置类型的预期初始化规则:

  • 默认初始化 (nullptr_t n;),使 n 的值未定义。正如 Johannes Schaub 正确指出的那样,这与最新版本的 Clang 编译得很好。
  • 值初始化 (nullptr_t n = nullptr_t();),将 n 初始化为 0。

此行为与例如int,所以nullptr_t 绝对是默认可构造的。 这里有趣的问题是:nullptr_t 具有未定义的值意味着什么?归根结底,nullptr_t 只有一个有意义的可能值,即nullptr。此外,类型本身仅通过nullptrliteral 的语义定义。这些语义是否仍然适用于未初始化的值?

为什么这个问题在实践中并不重要

您不想声明nullptr_t 类型的新变量。该类型唯一有意义的语义已经通过 nullptr 字面量表达,因此无论何时使用 nullptr_t 类型的自定义变量,都可以使用 nullptr。

实践中重要的是什么

唯一的例外是您可以采用nullptr_t 类型的非类型模板参数这一事实。对于这种情况,了解哪些值可以转换为nullptr_t 很有用,这在 4.10 中进行了描述:

空指针常量是整数类型的整数常量表达式 (5.19) 纯右值,其计算结果为零或 std::nullptr_t 类型的纯右值。 [...] 整数类型的空指针常量可以转换为std::nullptr_t类型的prvalue。

这基本上就是你所期望的:你可以写

nullptr_t n = 0;    // correct: 0 is special

但不是

nullptr_t n = 42;   // WRONG can't convert int to nullptr_t

gcc 4.6 和 Clang SVN 都做到了这一点。

【讨论】:

  • 您犯了一个错误,将类的正确性概括为非类。非类根本没有任何成员函数。
  • 非类类型可以在没有成员函数的情况下默认构造。这与拥有成员函数的意义上的默认构造函数无关,而是在实现默认可构造类型的概念的意义上。
  • @ComicSansMS - 再次取得良好进展。谢谢你。在使用值初始化语法时,它们是值初始化的,但我想我明白了你想说的话。无论如何,还有一点答案仍然具有误导性。未初始化的值绝对不必是nullptr。未初始化的bool 可能包含true、false 或任何其他位模式,并且使用此类“第三值”的计算是未定义的并且可能导致逻辑上“不可能”的分支路径。我怀疑但没有检查过这个新的未命名类型是否相同(编译器可能会警告)。
  • @etherice 我认为这对用户没有影响,但我认为它更容易掌握规范(个人意见 ofc)。
  • @JirkaHanika:还有一件事:当您说“...默认初始化,例如 int x = new int(); ...”时,您没有使用正确的术语。 int() 是 value-initialization,而不是 default-initialization。每 C++11 § 8.5.10:An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized. 每 8.5.7(总结):value-initialization for POD types is zero-initialization。每 8.5.11:If no initializer is specified for an object, the object is default-initialized. 每 8.5.6(总结):default-initialization for POD types is to do nothing at all..
【解决方案2】:

虽然nullptr 是语言本身的新增功能,但std::nullptr_t 只是未命名类型的别名,cstddef 中声明的别名如下:

typedef decltype(nullptr) nullptr_t;

虽然 nullptr_t 是一个 typedef 而不是语言关键字,它没有被列为基本类型,但它被指定为基本类型(而不是,例如,指针类型或类类型) .因此它没有默认构造函数,但您仍然可以像之前那样声明变量。您的变量未初始化,我想知道它的用途是什么以及您从clang 得到的错误消息。

另见here。

【讨论】:

  • 我认为在“基本类型”部分中讨论 nullptr_t 值的规范意味着 nullptr_t 是一种基本类型。没有明确列出哪些类型是基本类型,这意味着必须通过迭代“基本类型”中提到的所有类型来收集它们。无论如何,它是一种基本类型,这很有意义。
  • 请注意,您不能说“Type nullptr_t is a library typedef”。 typedef 确实不引入新类型。它只是为现有类型创建一个别名。 “nullptr_t”别名的类型是语言引入的。
  • @JohannesSchaub-litb - 好的,编辑了问题以使这一点更清楚。这一点是,nullptr 的类型在 C++ 程序中没有名称或别名,直到分配别名(最好使用标准库头文件完成)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
相关资源
最近更新 更多