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

for*_*818 82 c++ typedef

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

<cstddef>
<cstdio>
<cstring>
<ctime>
Run Code Online (Sandbox Code Playgroud)

而且,从C++ 11开始,也在

<cstdlib>
<cwchar> 
Run Code Online (Sandbox Code Playgroud)

首先,我想知道为什么会这样.这与DRY原则是否相矛盾?但是,我的问题是:

我应该使用上面哪个标题来使用size_t?它有关系吗?

Sea*_*ean 75

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

请注意,cstddef只保证定义std::size_t,即size_t在命名空间中定义std,尽管它可以在全局命名空间中提供此名称(实际上,简单size_t).

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

  • @SnakeDoc是的,那个标题是`cstddef`. (5认同)
  • 有没有保证`cstddef`中的`size_t`是相同的,并且总是和其他的一样?似乎应该有一个常见的头文件,其常见的定义如`size_t` ... (2认同)
  • @SnakeDoc,谁说他们自己定义?所有标准都说它是在包含那些标题后定义的,它并没有说它们都必须重新定义它.它们都可以包含`<cstddef>`,或者它们都可以包含一些只定义`size_t`的内部头文件. (2认同)
  • 答案中的 `csttddef` 是错字吗?也许`cstddef`是什么意思? (2认同)

Pix*_*ist 42

事实上,几个标题的概要(包括在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而不包括标题来获得类型

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

    using size_type = decltype(alignof(char));
    
    Run Code Online (Sandbox Code Playgroud)
  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*);
    
    Run Code Online (Sandbox Code Playgroud)

    不引入size_t,stdstd::size_t

    除非通过包括适当的标题声明了名称,否则提及stdstd::size_t形成不良.

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

    虽然,根据7.1.3/3,size_t内部的多个定义的出现std完全有效,但不允许按照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

  • @MaximEgorushkin:什么的.这就是未定义的行为,不是吗?它*可能*工作的点甚至它*在任意编译器上没有中断的点都不会使程序的行为根据标准定义.或者'fredoverflow'很好地[在这里](http://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior):"C++标准有唯一的投票,期限." (12认同)

vll*_*vll 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
Run Code Online (Sandbox Code Playgroud)

  • 不确定,但我认为编译时间确实重要,不是吗? (2认同)

Max*_*kin 7

你可以没有标题:

using size_t = decltype(sizeof(int));
using size_t = decltype(sizeof 1); //  The shortest is my favourite.
using size_t = decltype(sizeof "anything");
Run Code Online (Sandbox Code Playgroud)

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

sizeofand的结果sizeof...是一个类型为 的常量std::size_t。[ 注意:std::size_t在标准头文件<cstddef>(18.2) 中定义。— 尾注 ]

换言之,该标准要求:

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

另请注意,typedef在全局和std命名空间中进行此声明是完全没问题的,只要它与typedef具有相同typedef 名称的所有其他声明相匹配(在不匹配的声明中会发出编译器错误)。

这是因为:

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

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


怀疑论者说,这构成了向 namespace 中添加新类型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 中,现在有一种方法可以将标准的确切要求写成一个简单的声明。

  • @MaximEgorushkin 他们中有一半人不理解这段代码......它工作得很好。但是,我不喜欢这种方式:imo 最好包含一个标题并让标准定义它。 (16认同)
  • Tom 说,“有 6 个标准库头文件定义了同样的东西!太疯狂了!我们需要一个且只有一个 `size_t` 定义!” 一分钟后,Mary 说:“天哪!在标准库头文件中有 7 个 `size_t` 的定义,Tom 正在编辑一个项目头文件!在 3rd 方库中可能还有更多!” https://xkcd.com/927/ (11认同)
  • 伙计们,在您对完全正确的答案投反对票之前,至少要学习有效的语言。 (9认同)
  • @Sean 你写的没有任何意义。 (8认同)
  • @PanagiotisKanavos 你在说什么?这个 `size_t` 是无符号的。 (6认同)
  • 虽然这是 `size_t` 的一个可能定义,但这并没有回答 OP 的真正问题:就好像我要求声明 `FILE` 的标题,而你建议自己编写一个。 (6认同)
  • 说明:`sizeof` 的返回类型是 `size_t`。这段代码引入了一个类型,它等于 `sizeof` 的返回类型,并将其命名为 `size_t`。 (5认同)
  • @Morovaille 是的,否决票的数量表明有多少人不了解 C++。 (3认同)
  • @anatolyg 如果我想变得聪明,我会做`使用 size_t = decltype(sizeof(1));`,哈哈。 (3认同)
  • @Pixelchemist 这将是一个重复的声明,不会破坏 ODR。 (3认同)
  • 投反对票,虽然该帖子没有明确将 `size_t` 放在 `namespace std` 中,但 OP 的问题是关于访问 `std::size_t`。因此,它要么不回答 OP 的问题,要么建议 UB 这样做。(将这样的 typedef 注入 `namespace std` 是一个声明,你不能将这样的声明添加到命名空间 `std`。)你坚持认为这是合法的并不相关:即使你是正确的,标准也会是 *at最不明确的是 UB,并且在代码中添加不明确的 UB 几乎与明确的 UB 一样糟糕,除非有大的回报。 (3认同)
  • 没有有效的反对意见。@Yakk(等人)支持的论点是,将此声明添加到 `std` 是 UB。你只是不同意这一点。所有这些都是为了争取一个*充其量*解决不了任何问题的答案,只是想变得聪明。最好的情况是,你是对的(你不是),这是一个糟糕的答案。最坏的情况,这是错误的。 (3认同)
  • 尽管这似乎是对时间和精力的巨大浪费,但我被要求发表评论。从技术上讲,除非另有说明,否则向命名空间 std 中添加任何声明都是 UB。没有允许(重新)声明 `std::size_t` 的“其他指定”。但在这种情况下,它永远不会做错任何事,因为 `decltype(sizeof 1)` **is** `std::size_t`。但是我认为这样做比 `#include &lt;cstddef&gt;`(这是一个很小的标题)没有任何好处。 (3认同)
  • @tobi303 这个 typedef 与全局 `::size_t` typedef 完全相同。你可以多次 `typedef` 相同的类型,只要它被映射到相同的类型。 (2认同)
  • @underscore_d _defined_ 和 _advised_ 是不同的东西。 (2认同)
  • @NickyC:不,无论如何你都不能添加 `std::size_t` 定义,因为不允许添加到 `std` 。但根据您的目的(即实现某种定义“size_type”的容器),您可能想引入一个新的 typedef,然后这个答案是有效的方法。 (2认同)
  • 让我们[在聊天中继续讨论](http://chat.stackoverflow.com/rooms/109031/discussion-between-pixelchemist-and-maxim-egorushkin)。 (2认同)
  • @MaximEgorushkin您使用UB的方式似乎与“崩溃”或“段错误”一致,而不是“导致标准未指定行为的程序”。除少数情况外,“不应该”通过用户端代码向“std”添加符号是“有意”的。这并不是规格不足。你似乎在争论“当然,标准说不要这样做,但无论如何都要这样做,有什么害处”。这与“标准有错误”有着根本的不同。最后,假设“std::string::data()”是连续的*有很大的回报*,你的建议*没有*。 (2认同)