Dan*_*iel 1 c++ performance inline
我搜索了一些相关的问题(例如C++中的内联函数的好处?),但我仍有疑问.
如果内联函数只是"为编译器提供一个简单的机制来应用更多的OPTIMIZATIONS".
使用inline只满足一个定义规则.inline出于性能原因,请不要理会.
如果内联函数只是"为编译器提供一个简单的机制来应用更多的OPTIMIZATIONS".
如果我们谈论的是inline关键字,那么这不是什么意思inline.就像,完全一样.
所有这些inline 都确保函数不违反单一定义规则.它还可能为编译器提供一个提示,即编译器应该将代码内联,这可能会或可能不会提高速度,但编译器有权忽略该提示.事实上,在现代编译器中,他们会在很多时候忽略这一暗示.
编译器很可能不会内联代码的情况之一是具有大功能.这并不意味着不应声明函数inline- 但这取决于函数的声明,定义和使用方式.但是,它再次与性能无关.
在应用优化技术时,不要试图超越编译器.它使你的程序比你快得多.
让我们看看标准对所有这些内容的评价.
2 /带有
inline说明符的函数声明(8.3.5,9.3,11.3)声明了一个内联函数.的inline说明符指示,在呼叫的点的功能体的内联取代是优选的,以通常的函数调用机制的实施.在呼叫点执行此内联替换不需要实现; 但是,即使省略了这种内联替换,仍应遵守7.1.2定义的内联函数的其他规则.
这告诉我们这inline是一个请求编译器将代码内联,并且编译器不需要执行该替换.但这也告诉我们,无论编译器执行此内联替换,7.1.2中定义的"其他规则"仍然适用.
很久以前,C++编译器采用的优化技术相对于今天的编译器来说是原始的.在那些日子里,将它inline用作优化技术可能是有意义的.但是现在,编译器在优化代码方面要好得多.编译器应用的技术今天可以使代码比内联更快,即使函数实际上没有内联.(一个可能的例子,RVO.)
因此,最终的结果是,虽然7.1.2/2的初衷可能是给程序员一个手动优化技术,但现代编译器如此积极地进行优化,以至于这个原始意图在很大程度上没有实际意义.
所以剩下的inline就是那些"其他规则".那么那些其他规则是什么?(C++ 11措辞)
4 /内联函数应在每个使用过的翻译单元中定义,并且在每种情况下都应具有完全相同的定义(3.2).[注意:在定义出现在翻译单元之前,可能会遇到对内联函数的调用. - 结束注释]如果函数的定义在第一次声明为内联之前出现在翻译单元中,则程序格式不正确.如果在一个翻译单元中内联声明具有外部链接的功能,则应在其出现的所有翻译单元中内联声明; 无需诊断.具有外部链接的内联函数在所有翻译单元中应具有相同的地址.外部内联函数中的静态局部变量始终引用同一个对象.extern内联函数体中的字符串文字是不同翻译单元中的相同对象.[注意:出现在默认参数中的字符串文字不在内联函数的主体中,因为该表达式用于来自该内联函数的函数调用中. - 结尾注释]在extern内联函数体内定义的类型在每个翻译单元中都是相同的类型.
我们来看一个例子吧.假设我们有这个类template:
#ifndef FOO_H
#define FOO_H
#include <string>
#include <sstream>
class StringBuilder
{
public:
template <typename T> inline StringBuilder& operator<<(const T& t)
{
mStream << t;
return * this;
}
operator std::string () const;
private:
std::stringstream mStream;
};
StringBuilder::operator std::string() const
{
return mStream.str();
}
#endif
Run Code Online (Sandbox Code Playgroud)
如果我们#include这个文件main.cpp并使用StringBuilder,一切都很好:
#include <iostream>
#include <string>
int main()
{
double d = 3.14;
unsigned a = 42;
std::string s = StringBuilder()
<< "d=" << d << ", a=" << a;
std::cout << s << "\n";
}
Run Code Online (Sandbox Code Playgroud)
jdibling@hurricane:~/dev/hacks$ ./hacks
d=3.14, a=42
Run Code Online (Sandbox Code Playgroud)
但是如果我们想StringBuilder在第二个翻译单元中使用,我们就会遇到问题:
#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";
}
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)
StringBuilder::operator std::string()定义两次; 曾经一次main.cpp又一次other.cpp- 违反了"一个定义规则".
我们可以通过制作函数来解决这个问题inline:
class StringBuilder
{
public:
// [...]
inline operator std::string () const;
// ^^^^^^
private:
std::stringstream mStream;
};
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为现在operator std::string在两个具有完全相同定义的翻译单元上定义.它与在声明中直接定义函数具有相同的效果:
class StringBuilder
{
public:
operator std::string () const
{
return mStream.str();
}
private:
std::stringstream mStream;
};
Run Code Online (Sandbox Code Playgroud)