对内联行为C++的困惑

Confusion over inline behavior C++

本文关键字:C++      更新时间:2023-10-16

我正在学习C++,我被内联行为弄糊涂了。在cppreference上,我发现"包含在多个源文件中的函数必须是内联的"。他们的例子如下:

// header file
#ifndef EXAMPLE_H
#define EXAMPLE_H
// function included in multiple source files must be inline
inline int sum(int a, int b) 
{
return a + b;
}
#endif
// source file #2
#include "example.h"
int a()
{
return sum(1, 2);
}
// source file #1
#include "example.h"
int b()
{
return sum(3, 4);
}

这让我有点困惑——我认为ifndef保护程序正是在做这项工作,即在多次包含同一文件时防止出现问题。无论如何,我想测试一下,所以我准备了以下内容:

// Sum.h
#ifndef SUM_H
#define SUM_H
int sum(int a, int b);
#endif
// Sum.cpp
int sum(int a, int b){
return a + b;
}
// a.h
#ifndef A_H
#define A_H
int af();
#endif
// a.cpp
#include "sum.h"
int af(){
return sum(3, 4);
}
// b.h
#ifndef B_H
#define B_H
int bf();
#endif 
// b.cpp
#include "sum.h"
int bf(){
return sum(1, 2);
}
// main.cpp
#include "sum.h"
#include "a.h"
#include "b.h"
#include <iostream>
int main() {
std::cout << af() + bf();
}

这和预期的一样效果良好。然后,我在sum.cpp和sum.h中使用define将sum函数定义为内联函数,但它无法编译:

"sum(int, int)", referenced from:
bf() in b.cpp.o
af() in a.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

有人能帮我澄清一下吗?

带有#ifndef/#define/#endif#pragma once的include保护只防止包含每个翻译单元。

假设您有一个标题,如:

#pragma once
void Do() {}

以及两个执行include的CCD_ 3文件。如果您现在使用进行编译

g++ source1.cpp source2.cpp 

你会得到

xy行/文件中Do((的多重定义

每个源文件都将"单独"编译,因此从第一个翻译单元设置的第二个翻译单元看不到保护。两种翻译(汇编(都是完全独立完成的。因此,在这种情况下,include防护不会保护任何东西。

为此,可以将函数定义定义为inline。现在,这两个定义都将呈现给链接器,但标记为"弱"。链接器现在没有抱怨这两个定义,只接受其中一个(通常是最后一个!(,这并不重要,因为在这种情况下两者都是相同的。

因此,include guard的意义在于,您可以将一个文件多次包含到一个翻译单元中。通常只有在间接包含标头的情况下才会发生这种情况。假设a.h有函数定义,b.h和c.h每个都包括a.h。如果你的cpp文件现在包括b.h和c.h,它们都包括a.h.所以你有多个定义,没有include-guard。这是包含保护的用例。

如果函数定义为inline,但在所有使用翻译单位的情况下都不可见,则存在"未定义符号"的相反问题:

示例:

拥有该文件:

inline void sum() {}
void f1() { sum(); }

并用-O0编译产生、输出nm f1.o|c++filt

0000000000000000 T f1()
0000000000000000 W sum()

我们看到sum的符号被定义为弱,因此它可以在链接阶段多次出现。如果没有"看到"定义的第二个翻译单元将毫无问题地连接起来,也将使用它。

但是使用"-O3"你会得到:

0000000000000000 T f1()

这是编译器特有的实现!编译器可以提供内联函数。如果使用更高的优化级别,则通常不会。

通常:如果函数定义为inline,则在每个翻译单元使用之前,其定义必须对每个翻译单元可见!

然后我使用define将sum函数定义为sum.cpp和sum.h中的内联函数,但它无法编译:

当函数被声明为inline时,其定义必须在使用该函数的所有转换单元中可用。仅在一个翻译单元中定义函数是不正确的。这就是您看到链接器错误的原因。

将函数的定义移动到sum.h以解决链接器错误。

您应该仔细阅读文档中的语句:

包含在多个源文件中的函数必须是内联

它也这样说:

2(内联函数或变量的定义(由于C++17(必须存在于访问它的翻译单元中(不一定在访问点之前(。

您在示例中违反了该错误。

相关文章:
  • 没有找到相关文章