C++ 如果我将一个大函数声明为内联函数呢?
我搜索了一些相关的问题(例如),但仍然有问题 如果内联函数只是“为编译器应用更多优化提供一个简单的机制”C++ 如果我将一个大函数声明为内联函数呢?,c++,performance,inline,C++,Performance,Inline,我搜索了一些相关的问题(例如),但仍然有问题 如果内联函数只是“为编译器应用更多优化提供一个简单的机制” 那么我可以将每个函数都设置为内联函数吗 如果我错误地将一个函数设置为内联函数,那么在性能方面会发生什么 是否有任何阈值告诉我函数的大小不应该是内联函数 仅仅因为你让函数内联,并不意味着编译器必须让它内联。内联函数是对编译器的提示,而不是命令 编译器将使用一组度量标准,如优化级别、生成类型(调试或发布)、代码大小等来确定函数是否应内联。使用内联仅满足一个定义规则。出于性能原因,不要使用inli
仅仅因为你让函数内联,并不意味着编译器必须让它内联。内联函数是对编译器的提示,而不是命令
编译器将使用一组度量标准,如优化级别、生成类型(调试或发布)、代码大小等来确定函数是否应内联。使用
内联
仅满足一个定义规则。出于性能原因,不要使用inline
如果内联函数只是为了“为
编译器应用更多优化。”
如果我们谈论的是inline
关键字,那么这不是inline
的内容。一点也不
inline
所做的就是确保函数不违反一个定义规则。它还可能向编译器提供一个提示,即编译器应该以内联方式输出代码,这可能会提高速度,也可能不会提高速度,但编译器有权忽略该提示。事实上,在现代编译器中,他们会在很多时候忽略这一暗示
编译器很可能不会将代码内联输出的一种情况是使用大函数。这并不意味着函数不应该声明为内联的
,但是,这完全取决于函数的声明、定义和使用方式。但是,同样,它与性能没有任何关系
在应用优化技术时,不要试图智取编译器。它比你的程序快得多
让我们看看标准对所有这些都有什么规定 7.1.2函数说明符 2/A带有
内联说明符的函数声明(8.3.5、9.3、11.3)
声明一个内联函数。inline
说明符指示
在
调用点是常用函数调用机制的首选。
执行此内联替换不需要实现
在呼叫点;但是,即使此内联替换是
省略,7.1.2中定义的内联函数的其他规则应
仍然值得尊重
这告诉我们,inline
是对编译器的一个请求,要求将代码以内联方式输出,编译器不需要执行这种替换。但这也告诉我们,无论编译器执行这种内联替换,7.1.2中定义的“其他规则”仍然适用
有一段时间,很久以前,C++编译器使用的优化技术相对于今天的编译器来说是原始的。在那些日子里,使用inline
作为一种优化技术可能是有意义的。但是现在,编译器在优化代码方面做得更好了。编译器应用了一些技术,这些技术可以使代码比内联更快,即使函数实际上没有内联。(一个可能的例子是RVO。)
因此,最终的结果是,虽然7.1.2/2的初衷可能是给程序员一种手动优化技术,但现代编译器的优化如此之激进,以至于这个初衷在很大程度上没有意义
所以,内联所剩下的就是那些“其他规则”。那么,那些其他规则是什么呢?(C++11逐字)
4/应在每个翻译单元中定义内联函数
它是odr使用的,并且在
每例(3.2)。[注意:对内联函数的调用可能是
在其定义出现在翻译单元中之前遇到-
结束注释]如果函数的定义出现在翻译中
在第一次声明为内联之前,程序是
格式不正确。如果具有外部链接的函数在中声明为内联
一个翻译单元,应在所有翻译中声明为内联
其出现的单位;无需诊断。内联
具有外部链接的功能应在所有位置具有相同的地址
翻译单位。外部内联中的静态局部变量
函数总是引用同一个对象。字符串中的字符串文字
extern内联函数的主体是不同类型中的相同对象
翻译单位。[注意:默认值中出现的字符串文字
参数不在内联函数体中,这仅仅是因为
表达式用于来自该内联函数的函数调用。-结束
注意]外部内联函数体中定义的类型为
在每个翻译单元中使用相同的类型
让我们看一个例子。假设我们有这个类模板:
文件:foo.h
输出:
但是如果我们想在第二个翻译单元中使用StringBuilder
,我们会遇到一个问题:
文件:other.cpp
StringBuilder::operator std::string()
定义了两次;一次在main.cpp
中,另一次在other.cpp
中——这违反了一个定义规则
我们可以通过使函数内联来解决这个问题:
class StringBuilder
{
public:
// [...]
inline operator std::string () const;
// ^^^^^^
private:
std::stringstream mStream;
};
编译器输出:
这是因为现在运算符std::string
在两个翻译单元上定义的定义完全相同。它与直接在声明中定义函数具有相同的效果:
class StringBuilder
{
public:
operator std::string () const
{
return mStream.str();
}
private:
std::stringstream mStream;
};
如果我们假设编译器将在调用函数的位置粘贴内联代码,那么就会有大量重复代码
功能
jdibling@hurricane:~/dev/hacks$ ./hacks
d=3.14, a=42
#include <iostream>
#include <string>
#include "foo.h"
void DoSomethingElse()
{
unsigned long l = -12345;
long l2 = 223344;
std::string s = StringBuilder()
<< "l=" << l << ", l2=" << l2;
std::cout << s << "\n";
}
ninja: Entering directory `.'
[1/3] Building CXX object CMakeFiles/hacks.dir/main.o
[2/3] Building CXX object CMakeFiles/hacks.dir/other.o
[3/3] Linking CXX executable hacks
FAILED: : && /usr/bin/g++ -Wall -std=c++11 -g CMakeFiles/hacks.dir/main.o CMakeFiles/hacks.dir/other.o -o hacks -rdynamic -lboost_regex-mt && :
CMakeFiles/hacks.dir/other.o: In function `std::operator|(std::_Ios_Openmode, std::_Ios_Openmode)':
/home/jdibling/dev/hacks/foo.h:21: multiple definition of `StringBuilder::operator std::string() const'
CMakeFiles/hacks.dir/main.o:/home/jdibling/dev/hacks/foo.h:21: first defined here
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
class StringBuilder
{
public:
// [...]
inline operator std::string () const;
// ^^^^^^
private:
std::stringstream mStream;
};
ninja: Entering directory `.'
[1/3] Building CXX object CMakeFiles/hacks.dir/main.o
[2/3] Building CXX object CMakeFiles/hacks.dir/other.o
[3/3] Linking CXX executable hacks
class StringBuilder
{
public:
operator std::string () const
{
return mStream.str();
}
private:
std::stringstream mStream;
};