为什么不自动采用#pragma once?

Why isn't #pragma once automatically assumed?

专门告诉编译器只包含文件一次有什么意义?默认情况下没有意义吗?甚至有任何理由多次包含一个文件吗?为什么不假设呢?与特定硬件有关吗?

包括多次是可用的,例如,使用 X-macro 技术:

data.inc:

X(ONE)
X(TWO)
X(THREE)

use_data_inc_twice.c

enum data_e { 
#define X(V) V,
   #include "data.inc"
#undef X
};
char const* data_e__strings[]={
#define X(V) [V]=#V,
   #include "data.inc"
#undef X
};

我不知道还有什么其他用途。

您可以在文件中的任何地方使用 #include ,而不仅仅是在全局范围内 - 比如在函数内部(如果需要可以多次使用)。当然,丑陋且风格不佳,但可能且偶尔明智(取决于您包含的文件)。如果 #include 只是一次性的事情,那是行不通的。 #include 毕竟只是进行愚蠢的文本替换(剪切'n'粘贴),而不是你包含的所有内容都必须是头文件。您可能 - 例如 - #include 一个包含自动生成数据的文件,其中包含用于初始化 std::vector 的原始数据。喜欢

std::vector<int> data = {
#include "my_generated_data.txt"
}

并且 "my_generated_data.txt" 是构建系统在编译期间生成的东西。

或者我可能 lazy/silly/stupid 并将其放入文件中(非常 人为的示例):

const noexcept;

然后我做

class foo {
    void f1()
    #include "stupid.file"
    int f2(int)
    #include "stupid.file"
};

另一个稍微不那么做作的示例是一个源文件,其中许多函数需要在命名空间中使用大量类型,但您不想只在全局范围内说 using namespace foo;,因为那样会用很多其他你不想要的东西污染全局命名空间。所以你创建了一个文件 "foo" 包含

using std::vector;
using std::array;
using std::rotate;
... You get the idea ...

然后在源文件中执行此操作

void f1() {
#include "foo" // needs "stuff"
}

void f2() {
    // Doesn't need "stuff"
}

void f3() {
#include "foo" // also needs "stuff"
}

注意:我并不是提倡这样做。但它是可能的,并且在某些代码库中完成了,我不明白为什么它不应该被允许。它有它的用途。

也可能是您包含的文件的行为因某些宏 (#defines) 的值而异。因此,您可能希望在首先更改某些值后将文件包含在多个位置,这样您在源文件的不同部分会出现不同的行为。

不,这会严重阻碍图书馆作者等人的选择。例如,Boost.Preprocessor 允许使用预处理器循环,实现这些的唯一方法是多次包含同一文件。

并且 Boost.Preprocessor 是许多非常有用的库的构建块。

这里有多个相关问题:

  • 为什么 #pragma once 没有自动执行?
    因为有些情况下您想要多次包含文件。

  • 为什么要多次包含一个文件?
    其他答案中已经给出了几个原因(Boost.Preprocessor,X-Macros,包括数据文件)。我想添加一个 "avoid code duplication" 的特定示例:OpenFOAM 鼓励一种风格,其中 #include 在函数中使用点点滴滴是一个常见的概念。例如参见 [​​=14=] 讨论。

  • 好的,但为什么默认情况下没有选择退出?
    因为它实际上并没有被标准规定。 #pragma根据定义是特定于实现的扩展。

  • 为什么 #pragma once 还没有成为标准化功能(因为它得到广泛支持)?
    因为以与平台无关的方式确定什么是 "the same file" 实际上非常困难。 See this answer for more information.

您似乎是在假设语言中存在的“#include”功能的目的是为将程序分解为多个编译单元提供支持。 这是不正确的。

它可以扮演那个角色,但这不是它的预期目的。 C 是 originally developed as slightly higher-level language than PDP-11 Macro-11 重新实现 Unix 的程序集。它有一个宏预处理器,因为这是 Macro-11 的一个特性。它能够以文本方式包含来自另一个文件的宏,因为这是 Macro-11 的一个特性,他们正在移植到他们的新 C 编译器的现有 Unix 已经利用了这一特性。

现在证明“#include”有用将代码分成编译单元,因为(可以说)有点hack。然而,这个 hack 的存在意味着它变成了在 C 中完成的 The Way。存在一种方法的事实意味着 不需要 创建新方法来提供此功能,所以没有创造出更安全的东西(例如:不容易受到多重包含的影响)。由于它已经在 C 中,因此它与 C 的大部分其他语法和习语一起被复制到 C++ 中。

有人提议提供 C++ a proper module system,这样这个 45 年前的预处理器 hack 终于可以省掉了。我不知道这有多迫在眉睫。十多年来,我一直听说它正在开发中。

在我主要开发的产品的固件中,我们需要能够指定函数和 global/static 变量应该在内存中分配的位置。 Real-time 处理需要存在于芯片上的 L1 内存中,以便处理器可以直接快速访问它。不太重要的处理可以放在芯片上的 L2 内存中。任何不需要特别及时处理的东西都可以放在外部DDR中并通过缓存,因为慢一点也没关系。

#pragma to allocate where things go 是很长的 non-trivial 行。很容易弄错。弄错的效果是 code/data 会被静默放入默认 (DDR) 内存,而 that 的效果可能是 closed-loop 控制停止无缘无故工作很容易看出来。

所以我使用包含文件,其中只包含该编译指示。我的代码现在看起来像这样。

Header 文件...

#ifndef HEADERFILE_H
#define HEADERFILE_H

#include "set_fast_storage.h"

/* Declare variables */

#include "set_slow_storage.h"

/* Declare functions for initialisation on startup */

#include "set_fast_storage.h"

/* Declare functions for real-time processing */

#include "set_storage_default.h"

#endif

和来源...

#include "headerfile.h"

#include "set_fast_storage.h"

/* Define variables */

#include "set_slow_storage.h"

/* Define functions for initialisation on startup */

#include "set_fast_storage.h"

/* Define functions for real-time processing */

您会注意到同一文件的多个包含,即使只是在 header 中也是如此。如果我现在输入错误,编译器会告诉我它找不到包含文件 "set_fat_storage.h",我可以轻松修复它。

因此,在回答您的问题时,这是一个需要多重包含的真实、实际的示例。