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

根据cppreference.com size_t是在几个头文件中定义的,即

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

而且,自从C ++ 11以后,

 <cstdlib> <cwchar> 

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

我应该包含哪一个上面的头文件来使用size_t 这有什么关系吗?

假设我想尽量减less我导入的函数和types,我会使用cstddef因为它不声明任何函数,只声明6种types。 其他人则专注于特定的领域(string,时间,IO),这些领域可能与你无关。

请注意, csttddef只能保证定义std::size_t ,即在名字空间std定义size_t ,尽pipe它可以在全局名字空间(有效的,普通的size_t )中提供这个名字。

相比之下, stddef.h (也是C中可用的头文件)保证在全局名称空间中定义size_t ,并且可以提供std::size_t

事实上,几个头文件的概要(包含在C ++标准中)特别包含了size_t ,以及其他头文件定义了size_ttypes(基于C标准,因为<cX>头文件只是ISO C <Xh>没有指出size_t去除)。

然而, C ++标准 指的是用于定义std::size_t <cstddef>

  • 18.2types中
  • 5.3.3 Sizeof中
  • 3.7.4.2解除分配函数 (参见18.2)和
  • 3.7.4.1分配function (也参见18.2)中。

因此,因为<cstddef>只引入了types而没有函数,所以我会坚持这个头文件来使std::size_t可用。


请注意几件事情:

  1. std::size_t的types可以使用decltype获得,不包括头部

    如果你打算在你的代码中引入一个typedef(也就是说,因为你编写了一个容器并且想要提供一个size_type typedef),你可以使用全局的sizeofsizeof...或者alignof运算符来定义你的types,而不包括任何头文件因为这些操作符返回标准定义的std::size_t ,你可以在它们上使用decltype

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

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

     void* operator new(std::size_t); void* operator new[](std::size_t); void operator delete(void*); void operator delete[](void*); 

    不要引入size_tstdstd::size_t

    引用stdstd::size_t是格式化的,除非通过包含相应的头部来声明名称。

  3. 用户可能不会重新定义std::size_t尽pipe在同一个命名空间中可能有多个引用相同types的typedef。

    尽pipestdsize_t的多个定义的出现完全符合7.1.3 / 3的规定 ,但不允许按照17.6.4.2.1 / 1的规定namespace std添加任何声明:

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

    size_t添加一个适当的typedef到命名空间并不违反7.1.3,但它违反了17.6.4.2.1并导致未定义的行为。

    澄清:尽量不要误解7.1.3 ,也不要将声明或定义添加到std (除了一些typedef不是模板特化的模板特例)。 扩展namespace std

所有标准库头文件具有相同的定义; 你在自己的代码中包含哪一个并不重要。 在我的电脑上,我在_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 

你可以没有标题:

 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_ttypes的常量。 [注: std::size_t是在标准头文件<cstddef> (18.2)中定义的。 – 结束注意]

换句话说,标准要求:

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

还要注意,在全局名称空间和std名称空间中进行这种typedef声明是完全正确的,只要它匹配相同typedef-name的所有其他typedef声明(在非匹配声明上发出编译器错误)。

这是因为:

  • §7.1.3.1typedef -name不像类声明(9.1)或枚举声明所做的那样引入新types。

  • §7.1.3.3在给定的非类作用域中,可以使用typedef说明符来重新定义在该作用域中声明的任何types的名称,以引用它所指向的types。


对怀疑者说这构成了一个新的typesjoin到命名空间std ,而这样的行为被标准明确禁止,这就是UB,这就是它的全部; 我不得不说,这种态度等于无视和否认对潜在问题的更深入的理解。

标准禁止将新的声明和定义添加到命名空间std因为这样做可能会使标准库变得混乱,并且把所有的部分都踢掉。 对于标准作者来说,让用户专注于某些特定的事情并禁止为了好的措施而做其他事情是比较容易的,而不是禁止用户不应该做的每件事情,并且冒险丢失重要的东西(以及那条腿)。 他们过去曾经要求没有标准的容器应该用不完整的types实例化,而实际上有些容器可以做得很好(参见马修·H·奥斯特的“标准馆员:不完全types的容器” ):

最后,这一切看起来都太晦涩难懂了, 标准化委员会没有想到有什么select,只能说STL容器不应该用不完整的types工作。 为了好的措施,我们也把这个禁令应用到了标准库的其余部分。

……现在回想起来,现在这个技术已经被更好地理解了,那么这个决定似乎基本上是正确的。 是的,在某些情况下,可以实现一些标准的容器,这样它们可以用不完整的types实例化,但是在其他情况下也很难或不可能。 我们尝试使用std::vector进行的第一个testing大多是偶然事件,

鉴于语言规则要求std::size_t完全是decltype(sizeof(int)) ,所以要做namespace std { using size_t = decltype(sizeof(int)); } namespace std { using size_t = decltype(sizeof(int)); }是不会破坏任何东西的东西之一。

在C ++ 11之前,没有decltype ,因此没有办法在一个简单的语句中声明sizeof结果的types,而没有涉及大量的模板。 size_t在不同的目标体系结构上别名不同的types,但是,仅仅为sizeof的结果添加一个新的内置types并不是一个很好的解决scheme,也没有标准的内置typedef。 因此,当时最便携的解决scheme是将size_ttypes的别名放在一些特定的头文件中。

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