C++ C++;.cpp文件中的内联成员函数

C++ C++;.cpp文件中的内联成员函数,c++,function,inline,member,C++,Function,Inline,Member,我知道内联成员函数的定义应该放在头中。但是,如果无法将函数的实现放入头中,该怎么办?让我们以这种情况为例: 文件A.h #pragma once #include "B.h" class A{ B b; }; #pragma once #include "B.h" class A { B b; }; 文件B.h #pragma once class A; //forward declaration class B{ inline A getA(); }; #

我知道内联成员函数的定义应该放在头中。但是,如果无法将函数的实现放入头中,该怎么办?让我们以这种情况为例:

文件A.h

#pragma once
#include "B.h"

class A{
    B b;
};
#pragma once
#include "B.h"

class A {
    B b;
};
文件B.h

#pragma once

class A; //forward declaration

class B{
    inline A getA();
};
#pragma once

class B {
public:
    template<class T> inline T getA() {
        assert(NULL); // Use 'getA<A>()' template specialization only!
        return NULL;
    }
};

class A; // Forward declaration
template<> inline A B::getA<A>();
由于通知包含,我必须将
getA
的实现放入

B.cpp


编译器是否内联
getA
?如果是,那么哪个内联关键字是重要的(在头中的关键字还是在.cpp文件中的关键字)?是否有其他方法将内联成员函数的定义放入其.cpp文件中?

它不能,超出了B.cpp的范围。编译器以每个编译单元为基础进行操作,即它单独编译每个.cpp文件,因此如果它编译C.cpp,它将没有getA()的代码,需要执行函数调用并让链接器修复它(或者,如果它真的吸引了您的注意力并尝试内联,那么它最终将导致链接器错误。
inline
具有与
static
类似的特性)

唯一的例外是LTCG,即链接时代码生成,这在较新的编译器上可用

这种情况下的一种方法是使用另一个包含内联代码的头文件(有时称为*.inl文件)

编辑:至于哪个内联是相关的-它是类定义中的一个,即在头文件中。请记住,许多编译器对什么可以内联和应该内联有自己的想法。例如,gcc可以完全禁用内联(-O0),或者它可以内联任何它认为值得内联的内容(如-O3)。

引用自:

注意:必须确保 函数的定义(零件) 在{…})之间放置一个 头文件,除非函数是 仅在单个.cpp文件中使用 特别是,如果您将 函数的定义转换为.cpp文件 你可以从其他人那里称呼它 文件,您将得到一个“未解决” 来自链接器的“外部”错误

每当编译器发现内联函数的任何用法时,它都需要查看该内联函数的定义。如果内联函数放在头文件中,这通常是可能的

编译器会内联getA吗

否,除非在B.cpp本身中使用了
getA()

如果是,哪个内联关键字是重要的(标题中的关键字还是cpp中的关键字)

:仅在类主体之外的定义中

是否有其他方法将内联成员函数的定义放入其cpp文件中


不,至少我不知道。

现在,大多数编译器可以在链接时和编译时执行内联。如果您的函数可能从内联中受益,那么链接时优化器可能会这样做


当链接器访问它时,编译器输出的内联状态没有多少信息可用,只是编译器会将某些对象标记为可收集对象,例如,因为内联函数或类模板实例出现在多个编译单元中,或者当多个符号共享一个名称时,它会引发错误主函数被定义了两次。所有这些都不会影响它将生成的实际代码。

我将从相反的方向进行讨论

不要向函数中添加内联声明(除非需要)

唯一需要向函数/方法添加内联声明的时间是在头文件中但在类声明之外定义函数

X.h X.cpp 向您提供更具体的案例。

删除所有内联规范。它们没有做您认为它们正在做的事情。

我就是这样做的

文件A.h

#pragma once
#include "B.h"

class A{
    B b;
};
#pragma once
#include "B.h"

class A {
    B b;
};
文件B.h

#pragma once

class A; //forward declaration

class B{
    inline A getA();
};
#pragma once

class B {
public:
    template<class T> inline T getA() {
        assert(NULL); // Use 'getA<A>()' template specialization only!
        return NULL;
    }
};

class A; // Forward declaration
template<> inline A B::getA<A>();
#pragma一次
B类{
公众:
模板内联T getA(){
assert(NULL);//仅使用“getA()”模板专用化!
返回NULL;
}
};
类;//转发声明
模板内联A B::getA();
文件C.h

#pragma once
#include "A.h"
#include "B.h"

// Implement template specialization here!
template<> inline A B::getA<A>() { return A(); }
#pragma一次
#包括“A.h”
#包括“B.h”
//在这里实现模板专门化!
模板内联A B::getA(){返回A();}
只需包含“C.h”文件就可以使用getA()方法。原始代码的唯一变化是getA()方法必须定义为public而不是private


然而,正如你们中的许多人所解释的,这并不是真正有用的。

所以-在我的例子中-我只是简单地将B.cpp重命名为B.inl,并且在我通常会包含B.h的地方我会包含B.inl?我不会将B.cpp重命名为B.inl-我会创建一个单独的B.inl(或者最好是B_inline.h)然后,在你的B.cpp中,包括_inline.h文件。“它不可能,在B.cpp的范围之外…”-现在一些工具链已经有了链接时间优化(LTO),一些不可能的原因可能已经改变了。在使用GCC和其他一些编译器时,可以启用它。@jww不确定您是否阅读了第二段,但这就是我提到的LTCG/LTO:)这当然比我写这篇文章时的2010年更常见。虽然我个人还是喜欢写C++代码,通过将可链接函数放入头文件来帮助编译器优化。我不知道这是可能的。链接器是关心“inline”关键字,还是自己决定内联什么?我不太确定“most”部分。而且,LTCG的速度非常慢,就我个人而言,我喜欢对编译器的工作有更多的控制。@EboMike:你认为你拥有的控制只是一种幻觉。编译器在决定什么是值得内联的方面总是比您做得更好。(人类在进行所需分析时非常糟糕)。让编译器做它应该做的工作(内联)让人类做它擅长的事情(思考算法)。@Mat:所有现代编译器都忽略内联关键字(与内联代码相关)(除非你用编译器强制它们)