对内联行为C ++的困惑

时间:2018-07-24 14:47:12

标签: c++

我正在学习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函数定义为sum.cpp和sum.h中的内联函数,但编译失败:

"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)

有人可以帮我澄清一下吗?

3 个答案:

答案 0 :(得分:5)

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

让我们假设您有一个标题,例如:

#pragma once

void Do() {}

和两个包含文件的*.cpp文件。如果您现在使用

g++ source1.cpp source2.cpp 

您将获得

  

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

每个源文件都将被“单独”编译,因此从第一个翻译单元设置的第二个翻译单元中看不到防护。两种翻译(编译)都是完全独立的。因此,在这种情况下,包含保护将不会保护任何内容。

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

因此,包含保护具有一定的意义,即可以将一个文件多次包含一个文件。仅当您间接包含标头时,才会发生这种情况。假设a.h具有函数定义,b.h和c.h都包含a.h。如果您的cpp文件现在包含b.h和c.h,则它们都包含a.h。因此,在没有包含保护的情况下,您具有多个定义。这是包含防护的用例。

相反的问题是,如果函数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,则在每个翻译单元中使用该函数之前,必须先对其定义可见!

答案 1 :(得分:1)

  

然后我使用sum函数定义为sum.cpp和sum.h中的内联函数,并且编译失败:

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

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

答案 2 :(得分:0)

您应该在声明旁边仔细阅读documentation

  多个源文件中包含的

函数必须是内联的

它也这样说:

  

2)内联函数或变量的定义(自C ++ 17起)必须存在于对其进行访问的转换单元中(不一定在访问点之前)。

您在示例中违反的

会错误提示。