我应为`size_t`包括哪个标题?


95

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

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

而且,自C ++ 11起,

<cstdlib>
<cwchar> 

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

我应使用上述标题中的哪一个size_t?有关系吗?


1
打开相应的头文件并找到定义。
i486

33
@ i486-这是编写易碎的不可移植代码的好方法!
肖恩

3
@PanagiotisKanavos C标头是C ++标准库的一部分,可能在您所谓的“ true C ++”标头中没有重复。您的意思是什么?
underscore_d

14
我一直用<cstddef>std::size_t
Boiethios

4
@PanagiotisKanavos当然,通常这是一个很好的建议,但在这种情况下,它似乎并不相关-因为没有C ++的替代品std::size_t,OP并不主张使用传统的C函数,只是观察有关它们共享typedef的引用。我怀疑阅读此线程的任何人都会因此而被误导使用遗留类型/函数,但是如果您要确保他们不会使用遗留类型/函数,那就足够公平了!
underscore_d

Answers:


90

假设我想最小化导入的函数和类型,cstddef因为它不声明任何函数,而仅声明6种类型。其他的则专注于对您可能无关紧要的特定域(字符串,时间,IO)。

请注意,cstddef仅保证define std::size_t,即size_t在namespace中定义std,尽管它可以在全局命名空间中提供此名称(有效地是plain size_t)。

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


3
是否可以保证size_tfrom cstddef相同并且将始终与其他相同?似乎应该有一个具有相同定义的通用头文件,如size_t...
SnakeDoc '16

1
@SnakeDoc就像是魔术一样,这里的另一个答案已经通过“内部”标头完全观察到了这种情况。
underscore_d

5
@SnakeDoc是的,该标头是cstddef
user253751 '16

2
@SnakeDoc,谁说他们定义了自己的?所有标准说的是,将在包含这些标头之后定义它,而不是说它们都必须重新定义它。它们可以全部包含<cstddef>,也可以全部包含一些刚刚定义的内部标头size_t
Jonathan Wakely

1
csttddef在回答一个错字?也许 cstddef是什么意思?
ErikSjölund'17

46

实际上,几个标头的提要(包括在C ++标准中)size_t明确包括以及进一步的标头定义了类型size_t(基于C标准,因为<cX>标头只是ISO C <X.h>标头,但有明显的变化,size_t未标出删除)。

但是,C ++标准<cstddef>的是std::size_t

  • 18.2类型中
  • 5.3.3 Sizeof中
  • 3.7.4.2解除分配功能中(指18.2)和
  • 3.7.4.1分配功能中的内容(也请参见18.2)。

因此,由于<cstddef>只引入类型而没有函数,因此,我坚持使用此头文件来std::size_t使用。


注意几件事:

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

    如果你打算引进在你的代码的类型定义反正(即,因为你写的容器,并希望提供一个size_type类型定义),您可以使用全局sizeofsizeof...alignof运营商来定义你的类型,而不包括任何标题,因为在所有运营商必然也会返回std::size_t每标准定义,您可以decltype在它们上使用:

    using size_type = decltype(alignof(char));
  2. 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_tstdstd::size_t

    除非名称通过包含适当的标头声明,否则引用stdstd::size_t格式不正确。

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

    虽然,根据7.1.3 / 3完全存在size_t内部的多个定义,但根据17.6.4.2.1 / 1不允许添加任何声明:stdnamespace std

    如果C ++程序的声明或定义添加到名称空间std或名称空间std中的名称空间,则除非定义,否则其行为是不确定的。

    size_t名称空间添加适当的typedef 不会违反7.1.3,但是会违反17.6.4.2.1并导致未定义的行为。

    说明性:尽量不要误解7.1.3,不要在其中添加声明或定义std(除了一些类型特殊化的情况,其中typedef不是模板特殊化)。扩展namespace std


1
您会错过重复的typedef不会引入新类型的事实。它仅添加重复的typedef,这是完全有效的。
Maxim Egorushkin '16

@MaximEgorushkin:我不认为添加std重复定义的typedef 是无效的,因为重复的typedef是非法的。我声明这是非法的,因为您可能根本不会在其中添加定义namespace std-无论它们是否合法。
Pixelchemist '16

鉴于我们从所有这些标准报价中了解的信息,有什么可能打破?
Maxim Egorushkin '16

12
@MaximEgorushkin:什么都可以。这就是未定义的行为,不是吗?问题的关键在于它可以工作,甚至可以说,它的点在任何编译器上不会中断表示该程序的行为是根据标准定义的。或作为“fredoverflow”说得好听这里:“C ++标准具有唯一的投票阶段。”
Pixelchemist '16

我希望您运用批判性思维。可能会破坏什么?
Maxim Egorushkin '16

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

2
不确定,但是我认为这对编译时间很重要,不是吗?
idclev 463035818 '16

@ tobi303不适用于此特定问题。是的,您可能添加了比必要的更大的标头,但随后您已经在C ++项目中添加了C标头。为什么首先需要size_t
Panagiotis Kanavos '16

使用OS宏嗅探来定义不是一个好主意size_t。您可以更方便地将其定义为using size_t = decltype( sizeof( 42 ) )。但是没有必要,因为<stddef.h>成本几乎为零。
干杯和健康。-Alf 2016年

4

您可以不使用标题:

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.1typedef -name不会像类声明(9.1)或枚举声明那样引入新类型。

  • §7.1.3.3在给定的非类范围内,typedef可以使用说明符来重新定义在该范围内声明的任何类型的名称,以引用它已经引用的类型。


持怀疑态度的人说,这是在名称空间中添加了新类型 std,而该行为已被标准明确禁止,这就是UB,仅此而已;我必须说,这种态度等于忽略和否认对根本问题的更深了解。

该标准禁止将新的声明和定义添加到名称空间中,std因为这样做可能会使用户弄乱标准库并全力以赴。对于标准编写者而言,让用户专注于一些特定的事情并禁止采取其他措施以达到良好的效果是容易的,而不是禁止用户不应做的每件事,并冒着错过重要的事情(和那条腿)的风险。他们在过去要求不使用不完整类型实例化标准容器的情况下就这样做了,而实际上某些容器可以做到(请参阅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中,现在有一种方法可以将标准的确切要求记为一个简单的声明。


6
@Sean您写的内容没有任何意义。
Maxim Egorushkin '16

15
@MaximEgorushkin他们中的一半不了解此代码...完美地工作。但是,我不喜欢这种方式:imo最好包含一个标头并让标准定义它。
Boiethios

9
伙计们,至少在您对完全正确的答案进行否决之前,先学习effing语言。
弗雷德里克·哈米迪

11
汤姆说:“有6个标准库头定义了同一件事!太疯狂了!我们只需要一个size_t!定义!” 一分钟后,玛丽说:“天哪!size_t在标准库头中有7个定义,在汤姆正在编辑的项目头中!第三方库中可能还有更多的定义!” xkcd.com/927

6
尽管这可能是的定义size_t,但这并不能回答OP的真正问题:好像我要求在标头中FILE声明该标头,并且建议您编写自己的标头。
edmz '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.