我正在维护一个大型模板类库,它们基于以下任意一个执行代数计算float
or double
类型。许多类都有访问器方法(getter 和 setter)和其他运行少量代码的函数,因此当编译器找到它们的定义时,需要将这些函数限定为内联。相比之下,其他成员函数包含复杂的代码,因此最好调用而不是内联。
函数定义的很大一部分位于标头中,实际上位于标头包含的 .inl 文件中。但也有许多类的函数定义通过显式实例化而愉快地存在于 .cpp 文件中float
and double
,这对于图书馆来说是一件好事(here https://stackoverflow.com/questions/2351148/explicit-instantiation解释了原因)。最后,有相当多的类的函数定义分散在 .inl 文件(访问器方法)和 .cpp 文件(构造函数、析构函数和繁重计算)中,这使得它们都非常难以维护。
仅当我知道一种可靠的方法来防止某些函数被内联时,我才会将所有类实现放在 .inl 文件中,或者在 .cpp 文件中,如果inline
关键字可能强烈建议编译器内联某些函数,当然,事实并非如此。我确实希望库中的所有函数定义都驻留在 .cpp 文件中,但由于访问器方法在整个库中广泛使用,因此我必须确保它们在引用时内联,而不是调用。
因此,在这方面,我的问题是:
用 标记模板函数的定义是否有意义inline
鉴于我最近了解到的事实here https://stackoverflow.com/questions/11416747/multiple-definitions-of-a-non-template-class-vs-a-template-class,无论它是否标记为内联,编译器都会自动将其限定为内联inline
or not?
And 最重要的是,因为我想收集模板类的所有成员函数的定义一起在一个文件中,最好是 .inl 或 .cpp(在 .cpp 的情况下使用显式实例化)still能够提示编译器(MSVC 和 GCC)应该内联哪些函数and这不应该,确定这样的事情是否可以通过模板函数实现,我怎样才能实现这个目标,或者,如果真的没有办法(我希望有),最佳的折衷方案是什么?
----------
EDIT1:我知道inline
关键字只是建议编译器内联函数。
EDIT2:我确实知道。我喜欢向编译器提出建议。
EDIT3:我还是知道的。这不是问题的重点。
----------
鉴于一些新的信息,还有第三个问题与第二个问题同时出现。
3.如果现在的编译器如此聪明,他们可以更好地选择应该内联哪些函数以及应该调用哪些函数and能够进行链接时代码生成和链接时优化,这有效地允许他们在链接时查看位于 .cpp 的函数定义,以决定其内联或调用的命运,那么也许一个好的解决方案就是简单地移动所有定义到各自的 .cpp 文件中?
----------
那么结论是什么呢?
首先,我感谢 Daniel Trebbien 和 Jonathan Wakely 的结构化且有理有据的回答。两者都投了赞成票,但必须只选择一个。然而,给出的答案都没有为我提供可接受的解决方案,因此所选择的答案恰好是对我做出最终决定稍有帮助的答案,接下来将向感兴趣的人解释其详细信息。
好吧,由于我一直更看重代码的性能,而不是维护和开发的方便程度,因此在我看来,最可接受的折衷方案是移动每个的所有访问器方法和其他轻量级成员函数。将模板类放入相应标头包含的 .inl 文件中,并用以下标记标记这些函数inline
尝试为编译器提供良好的提示(或内联强制的关键字),并将其余函数移至相应的 .cpp 文件中。
将所有成员函数定义都放在 .cpp 文件中会阻碍轻量级函数的内联,同时会引发链接时优化的一些问题,正如 MSVC 的 Daniel Trebbien(在较早的开发阶段)和 GCC 的 Jonathan Wakely 所确定的那样(目前的发展阶段)。将所有函数定义都放在头文件(或 .inl 文件)中并不比将每个类的实现分类到 .inl 和 .cpp 文件中的总体好处加上此决定的额外副作用:它将确保只有原始访问器方法的代码对库的客户端可见,而更多有趣的东西隐藏在二进制文件中(确保这不是主要原因,但是,对于熟悉软件库的任何人来说,这一点都是显而易见的)。任何不需要由库的包含文件公开并由其类私有使用的轻量级成员函数都可以在该类的 .cpp 文件中进行定义,而其声明/定义则包含inline
鼓励函数的内联状态(尚不知道关键字是否应该出现在两个位置,或者在这种特殊情况下只出现在一个位置)。