我应该为`size_t`包含哪个标题?

时间:2016-04-13 09:45:44

标签: c++ typedef

根据cppreference.com size_t在几个标题中定义,即

<cstddef>
<cstdio>
<cstring>
<ctime>

而且,自C ++ 11以来,也在

<cstdlib>
<cwchar> 

首先,我想知道为什么会这样。这不是DRY原则的矛盾吗?但是,我的问题是:

我应该包含上述哪个标题才能使用size_t?是否重要?

4 个答案:

答案 0 :(得分:68)

假设我想最小化我导入的函数和类型,我会使用cstddef,因为它没有声明任何函数,只声明了6种类型。其他人专注于对您而言无关紧要的特定领域(字符串,时间,IO)。

请注意,cstddef仅保证定义std::size_t,即在命名空间size_t中定义std,尽管可能提供此名称也在全局命名空间中(实际上是普通size_t)。

相比之下,stddef.h(也是C中可用的标头)保证在全局命名空间中定义size_t,而可能也提供std::size_t

答案 1 :(得分:40)

事实上,几个标题的概要(包含在C ++标准中)特别包括size_t以及其他标题定义类型size_t(基于C标准<cX>标题只是ISO C <X.h>标题,其中包含已注明的更改,其中未指明删除size_t

C ++标准但是,<cstddef> 的定义中引用std::size_t

  • in 18.2 Types
  • in 5.3.3 Sizeof
  • in 3.7.4.2释放函数(指18.2)和
  • in 3.7.4.1分配函数(也指18.2)。

因此,由于<cstddef>只引入类型而没有函数,我会坚持使用此标题使std::size_t可用。

请注意以下几点:

  1. std::size_t的类型可以使用decltype获得,但不包含标题

    如果您计划在代码中引入typedef(即因为您编写了一个容器并希望提供size_type typedef),您可以使用全局sizeof,{{1} }或sizeof...运算符定义您的类型而不包含任何标题,因为这些运算符按标准定义返回alignof,您可以对它们使用std::size_t

    decltype
  2. using size_type = decltype(alignof(char)); 本身并不全局可见,但带有std::size_t参数的函数是。

    隐式声明的全局分配和释放函数

    std::size_t

    不要介绍void* operator new(std::size_t); void* operator new[](std::size_t); void operator delete(void*); void operator delete[](void*); size_tstd

      

    引用std::size_tstd的格式不正确,除非通过包含相应的标题声明了名称。

  3. 用户可能无法重新定义std::size_t,尽管可能有多个typedef在同一命名空间中引用相同的类型。

    尽管std::size_tsize_t的多个定义在 7.1.3 / 3 中完全有效,但不允许向{{1>}添加任何声明{1}}根据 17.6.4.2.1 / 1

      

    如果C ++程序向名称空间std或命名空间std中的命名空间添加声明或定义,则它是未定义的,除非另有说明。

    为命名空间添加std的正确typedef不会违反 7.1.3 ,但它确实违反 17.6.4.2.1 并导致未定义的行为

    澄清:尽量不要误解 7.1.3 ,不要向namespace std添加声明或定义(除了少数模板专业化情况,其中typedef不是模板专业化)。 Extending the namespace std

答案 2 :(得分:9)

所有标准库头文件都有相同的定义;你在自己的代码中包含哪一个并不重要。在我的计算机上,我在_stddef.h中有以下声明。您列出的每个文件都包含此文件。

/*
   Define the size_t type in the std namespace if in C++ or globally if in C.
   If we're in C++, make the _SIZE_T macro expand to std::size_t
*/

#if !defined(_SIZE_T) && !defined(_SIZE_T_DEFINED)
#  define _SIZE_T_DEFINED
#if defined(_WIN64)
   typedef unsigned __int64 size_t;
#else
   typedef unsigned int size_t;
#endif
#  if defined(__cplusplus)
#    define _SIZE_T std::size_t
#  else
#    define _SIZE_T size_t
#  endif
#endif

答案 3 :(得分:3)

你可以没有标题:

using size_t = decltype(sizeof(int));
using size_t = decltype(sizeof 1); //  The shortest is my favourite.
using size_t = decltype(sizeof "anything");

这是因为C ++标准要求:

  

sizeofsizeof...的结果是std::size_t类型的常量。 [注意:std::size_t在标准标题<cstddef>(18.2)中定义。 - 结束说明]

换句话说,标准要求:

static_assert(std::is_same<decltype(sizeof(int)), std::size_t>::value,
              "This never fails.");

另请注意,只要它与同一个typedef声明匹配,就可以在全局和std命名空间中进行此typedef声明。 > typedef-name (在非匹配声明上发出编译器错误)。

这是因为:

  • §7.1.3.1 typedef-name 不会像类声明(9.1)或枚举声明那样引入新类型。

  • §7.1.3.3在给定的非类作用域中,typedef说明符可用于重新定义在该作用域中声明的任何类型的名称,以引用它已引用的类型。

怀疑论者认为这构成了在命名空间std中添加新类型,并且标准明确禁止这种行为,这就是UB,这就是它的全部内容;我不得不说,这种态度等于忽视并否认对基本问题有更深刻的理解。

标准禁止在命名空间std中添加新的声明和定义,因为通过这样做,用户可能会弄​​乱标准库并将其整个腿关闭。对于标准作者来说,更容易让用户专注于一些特定的事情并禁止做任何其他事情,而不是禁止用户不应做的每一件事,并冒险丢失重要的事情(和那条腿)。他们过去曾经要求不要用不完整的类型来实例化标准容器,而事实上一些容器可以做到(见The Standard Librarian: Containers of Incomplete Types by Matthew H. Austern):

  

......最后,这一切似乎都太模糊,太难理解了;标准化委员会认为没有任何选择,只是说STL容器不应该用于不完整类型。为了更好地衡量,我们也将禁令应用于标准库的其余部分。

     回想起来,现在技术得到了更好的理解,这个决定似乎基本上是正确的。是的,在某些情况下,可以实现一些标准容器,以便可以使用不完整的类型对它们进行实例化 - 但是很明显,在其他情况下,它很难或不可能。我们尝试使用std::vector进行的第一次测试恰好是一个简单的案例。

鉴于语言规则要求std::size_t完全decltype(sizeof(int)),执行namespace std { using size_t = decltype(sizeof(int)); }就是其中一项不会破坏任何内容的事情。

在C ++ 11之前,没有decltype,因此没有办法在一个简单的语句中声明sizeof的结果,而不会涉及到大量的模板。 size_t在不同的目标体系结构上别名不同的类型,但是,仅为sizeof的结果添加新的内置类型并不是一个优雅的解决方案,并且没有标准的内置typedef 。因此,当时最便携的解决方案是将size_t类别别名放在某个特定的标题中并记录下来。

在C ++ 11中,现在有一种方法可以将标准的确切要求写为一个简单的声明。