有充分的理由在 h 文件而不是 cpp 文件中使用 include guards 吗?

Is there a good reason to use include guards in h files instead of cpp files?

例如,根据我对 C++ 和 clang++ 的基本理解,我不明白为什么以下设置不是更可取的:

似乎使用 headers 的变体会产生更多的输出,需要更具体的编译器设置,并且需要在函数声明中重复。

clang++ --std=c++2a Start.cpp -o Start.o; ./Start.o

Start.cpp

#include "A.cpp"
#include "B.cpp"

int main (
    int argc,
    char** argv
) {
    A();
    B();

    return 0;
}

A.cpp

#ifndef A_H
#define A_H

#include <stdio.h>
#include "C.cpp"

void A() {
    printf("A\n");
    C();
}

#endif

B.cpp

#ifndef B_H
#define B_H

#include <stdio.h>
#include "C.cpp"

void B() {
    printf("B\n");
    C();
}

#endif

C.cpp

#ifndef C_H
#define C_H

#include <stdio.h>

void C() {
    printf("C\n");
}

#endif

如果您 link 该目标文件与另一个包含其中一个 cpp 文件的目标文件,则该程序将违反一个定义规则,因为这些文件包含非内联函数的定义。

此外,通常将cpp文件和link一起编译。如果您编译这些 cpp 文件中的任何一个,同时将其包含到另一个翻译单元中,您将遇到违反 ODR 的情况。

要解决这个问题,如果您将一个文件包含到其他文件中,那么您通常应该确保包含的文件(即头文件)不包含任何在包含到多个翻译单元中时会违反 ODR 的内容。

此外,最好遵循常用的命名方案。对头文件使用 cpp 扩展名与所有常见的命名方案相反。

Is there a good reason to use include guards in h files instead of cpp files?

无论您如何命名头文件,您都应该始终在所有头文件中使用 include guard。 (虽然从技术上讲,某些头球不需要头球后卫,但简单地使用头球后卫比跟踪是否需要它更容易)。


现在,如果您想知道是否可以将所有函数定义放入一个翻译单元中:是的,可以。这有优点也有缺点:

单个 TU 的优点:由于无需重复编译模板和其他内联函数,因此从头开始编译时间更快。也有更好的优化,因为没有翻译单元边界会阻止优化 - 这种优势被跨 TU 边界工作的 link 时间优化削弱了。

缺点:对程序的任何更改只会导致重新编译修改后的翻译单元。当整个程序只有一个翻译单元时,那么任何更改,无论多么小,都需要重新编译整个程序。这是非常不希望的。 link 时间优化减少了这个缺点,这可能导致 linking 非常慢,以至于从重用翻译单元节省的时间变得微不足道。