在2016年奥卢ISO C++标准会议上,一项名为Inline Variables的提案被标准委员会投票选为C++ 17.
通俗地说,什么是内联变量,它们如何工作以及它们对什么有用?如何声明,定义和使用内联变量?
Che*_*Alf 108
提案的第一句话:
"该
inline说明符可以被应用到变量以及给函数.
inline应用于函数的gu保证效应是允许在多个翻译单元中以外部链接相同地定义函数.对于实践,这意味着在标题中定义函数,可以包含在多个翻译单元中.该提案将这种可能性扩展到变量.
因此,实际上,(现在接受的)提议允许您使用inline关键字在头文件中定义外部链接const命名空间范围变量或任何static类数据成员,以便在包含该标题时产生的多个定义链接器可以使用多个翻译单元 - 它只选择其中一个.
直到并包括C++ 14,为了支持static类模板中的变量,内部机器已经存在,但是没有方便的方法来使用该机器.一个人不得不采取类似的技巧
template< class Dummy >
struct Kath_
{
static std::string const hi;
};
template< class Dummy >
std::string const Kath_<Dummy>::hi = "Zzzzz...";
using Kath = Kath_<void>; // Allows you to write `Kath::hi`.
Run Code Online (Sandbox Code Playgroud)
从C++ 17开始,我相信一个人可以写作
struct Kath
{
static std::string const hi;
};
inline std::string const Kath::hi = "Zzzzz..."; // Simpler!
Run Code Online (Sandbox Code Playgroud)
...在头文件中.
该提案包括措辞
"可以在类定义中定义内联静态数据成员,并且可以指定大括号或等于初始化程序.如果使用说明
constexpr符声明成员,则可以在没有初始化程序的命名空间范围内重新声明该成员(此用法已弃用;请参阅DX).其他静态数据成员的声明不应指定itializer中的括号或等号
...允许将上述内容进一步简化为
struct Kath
{
static inline std::string const hi = "Zzzzz..."; // Simplest!
};
Run Code Online (Sandbox Code Playgroud)
此外,说明 ?constexpr符暗示 inline 静态数据成员和函数.
注意:
¹对于函数inline也有一个关于优化的提示效果,编译器应该更喜欢用直接替换函数的机器代码来替换此函数的调用.这种暗示可以忽略不计.
Phi*_*ßen 12
内联变量与内联函数非常相似.它通知链接器只有一个变量实例应该存在,即使在多个编译单元中看到变量也是如此.链接器需要确保不再创建副本.
内联变量可用于在仅头文件库中定义全局变量.在C++ 17之前,他们必须使用变通方法(内联函数或模板黑客).
例如,一种解决方法是使用具有内联函数的Meyer's单例:
inline T& instance()
{
static T global;
return global;
}
Run Code Online (Sandbox Code Playgroud)
这种方法存在一些缺点,主要是在性能方面.模板解决方案可以避免这种开销,但很容易弄错.
使用内联变量,您可以直接声明它(不会出现多重定义链接器错误):
inline T global;
Run Code Online (Sandbox Code Playgroud)
除了头文件库之外,还有其他内联变量可以帮助的情况.Nir Friedman在CppCon的演讲中介绍了这个主题:C++开发人员应该了解全局变量(以及链接器).关于内联变量和变通方法的部分从18m9开始.
简而言之,如果你需要声明在编译单元之间共享的全局变量,将它们声明为头文件中的内联变量是很简单的,并避免了前C++ 17解决方法的问题.
(例如,如果您明确希望进行延迟初始化,那么Meyer的单例仍有用例.)
最小的可运行示例
强大的C ++ 17功能使我们能够:
constexpr:如何声明constexpr extern?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
Run Code Online (Sandbox Code Playgroud)
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
Run Code Online (Sandbox Code Playgroud)
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
Run Code Online (Sandbox Code Playgroud)
编译并运行:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
Run Code Online (Sandbox Code Playgroud)
另请参阅:内联变量如何工作?
内联变量的C ++标准
C ++标准保证地址相同。C ++ 17 N4659标准草案 10.1.6“内联说明符”:
6具有外部链接的内联函数或变量在所有翻译单元中应具有相同的地址。
cppreference https://zh.cppreference.com/w/cpp/language/inline解释说,如果static未给出,则具有外部链接。
GCC内联变量实现
我们可以观察到它是如何实现的:
nm main.o notmain.o
Run Code Online (Sandbox Code Playgroud)
其中包含:
main.o:
U _GLOBAL_OFFSET_TABLE_
U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i
notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
Run Code Online (Sandbox Code Playgroud)
并man nm说u:
“ u”符号是唯一的全局符号。这是对ELF符号绑定的标准集合的GNU扩展。对于这样的符号,动态链接器将确保在整个过程中只有一个使用此名称和类型的符号。
因此,我们看到有专用的ELF扩展程序。
C ++ 17之前的版本: extern const
在C ++ 17之前和C中,我们可以使用来实现非常相似的效果extern const,这将导致使用单个内存位置。
缺点inline是:
constexpr使用这种技术不可能创建变量,只能inline允许:如何声明constexpr extern?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
Run Code Online (Sandbox Code Playgroud)
notmain.cpp
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
Run Code Online (Sandbox Code Playgroud)
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
Run Code Online (Sandbox Code Playgroud)
C ++ 17之前的标头仅替代品
这些不如extern解决方案那样好,但是它们可以工作并且仅占用一个内存位置:
一个constexpr函数,因为constexpr隐含inline并inline 允许(强制)定义出现在每个翻译单元上:
constexpr int shared_inline_constexpr() { return 42; }
Run Code Online (Sandbox Code Playgroud)
我敢打赌,任何不错的编译器都会内联该调用。
您还可以使用const或constexpr静态整数变量,如下所示:
#include <iostream>
struct MyClass {
static constexpr int i = 42;
};
int main() {
std::cout << MyClass::i << std::endl;
// undefined reference to `MyClass::i'
//std::cout << &MyClass::i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
但是您不能做诸如获取其地址之类的事情,否则它会变得奇怪,请参见:https : //en.cppreference.com/w/cpp/language/static “恒定静态成员”和定义constexpr静态数据成员
C
在C中,情况与C ++ pre C ++ 17相同,我在以下位置上载了一个示例:“静态”在C中是什么意思?
唯一的区别是在C ++中const隐含static全局变量,但在C中却不隐含:“静态常量”与“常量”的C ++语义
有什么办法可以完全内联吗?
TODO:有什么方法可以完全内联变量,而无需使用任何内存?
就像预处理器一样。
这将需要某种方式:
有关:
在Ubuntu 18.10,GCC 8.2.0中进行了测试。
| 归档时间: |
|
| 查看次数: |
30722 次 |
| 最近记录: |