C++ "size_t"不需要"cstddef"标题?

C++ "size_t" doesn't need "cstddef" header?

本文关键字:cstddef 标题 不需要 size C++      更新时间:2023-10-16

我正在C++入门书中学习C++,它说"size_t"是在"cstddef"标题中定义的,但在这个练习中:

#include <iostream>
using namespace std;
int main()
{
int ar[10];
for (size_t x = 0; x < 10; ++x)
ar[x] = x;
for (auto a : ar)
cout << ar[a] << " ";
cout << endl;
return 0;
}

这不包括标头,Visual Studio 2017(和c ++ shell)编译程序没有错误。

size_t实际上是一个灰色地带。std::size_tsizeof的结果类型,但sizeof是一个内置运算符,您可以在没有任何#include的情况下使用。考虑这个完整的小程序:

// no includes, no using namespace std
int main()
{
auto x = sizeof(int); // x is std::size_t
}

最重要的是,Visual C++在这里的表现总是有点奇怪。即使在最新版本的编译器中使用/permissive- /std:c++latest等设置,它仍然允许以下非法代码:

// no includes, no using namespace std
int main()
{
size_t i  = 0;
}

事实上,它甚至允许这样做

// no includes, no using namespace std
int main()
{
int ar[10];
for (size_t x = 0; x < 10; ++x)
ar[x] = x;
for (auto a : ar)
;
return 0;
}

尽管如此,其他人关于间接包含标题的说法是正确的。确切地说,C++标准在§20.5.5.2中对标准库标头有如下说明:

C++标头可能包含其他C++标头。

这意味着视觉C++无论如何都会在您的情况下正确运行。一旦你包含了<iostream>,实现就可以自由地间接包含定义std::size_t的六个标准C++头中的一个,而你的using namespace std;(这是邪恶的)会完成其余的工作。

C++标准甚至保证了一些此类间接包含,但这不是其中之一,因此为了使您的代码与其他编译器兼容,强烈建议您包含<cstddef>或其他保证std::size_t之一。

允许标准标头包含其他标准标头。由于不同实现中的标头具有不同的依赖项,因此您仍应尝试显式包含所需的所有内容。例如,你的程序可能不会建立在Linux的libstdc++或macOS的libc++上。

标准标头通常会包含其他标准标头,因此在许多情况下,您可能会侥幸不包括正确的标头。

问题是标头之间的这种关系不在标准中,例如依赖于实现。如果未包含必需的标头,则代码可能适用于一个编译器,但可能会在另一个编译器上运行。

通常,如果您知道标头 X 中定义的内容在其定义中使用类型 T,则可能会认为在包含 X 后类型 T 将可用。例如,<vector>使用std::size_t作为std::vector定义的一部分,因此它通常包括<cstddef>

有时可以使用前向声明来避免包含其他标准标头。但这只能通过类和结构来实现,而不是 typedef。

一些实现,例如GNU Libc,更加严格,并试图避免在内部包含标准标头。其他人,如MSVC,则不那么严格。与 GNU Libc 一起使用的代码通常适用于 MSVC。

很难验证您的代码是否包含您需要的所有内容。有一些工具可以帮助您找到缺少的包含,但使用多个编译器构建代码通常是查找这些问题的最佳方法。

不,std::size_t可能在多个标头中定义:

Defined in header <cstddef>
Defined in header <cstdio>
Defined in header <cstdlib>
Defined in header <cstring>
Defined in header <ctime>
Defined in header <cwchar>

另请注意,<iostream>本身包括其他标头。

首先,std::size_t被定义为许多标准标头:<cstddef><cstdio><cstdlib><cstring><ctime><cwchar>

如果不#include其中之一,则不需要编译代码。

实际上,标准库的许多实现都有各种标准标头相互#include,在这种情况下,您的代码将编译。 定义std::size_t的标头(甚至它们的 C 标头等效项,如<stddef.h>)包含在C++标准库中的其他标头中,这是很常见的,但不能保证。

更具体地说,标准库工作的许多部分都使用动态内存分配(标准容器、流缓冲区等)。 一个明显但不是必需的实现选择是它们使用size_t来表示大小。 例如,像std::vector这样的标准容器具有关联的size_type,并且std::vector<any_type>::size_type可以而且通常等同于std::size_t

<iostream>(连同它自动#includes的标头)不需要包含定义std::size_t的标头,但 - 同样 - 标准中没有任何内容不允许它。

最后,这取决于您希望对可移植性保持谨慎程度。 如果<iostream>引入了特定编译器的std::size_t定义,则代码将编译。 编译器的未来版本可能会更新标准标头以更改它(尽管在实践中不太可能,但并非不可能)。 如果您打算将来将代码移植到另一个实现(编译器和标准库),则更有可能需要修改代码。

实际上,显式#includeing<cstddef>或任何其他定义std::size_t的标头并没有什么坏处。 这样,使用std::size_t的代码将进行编译,而不管其他标准标头在实现之间有何不同。