Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 100 additions & 66 deletions lang/cpp17/inline_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

## 概要

外部リンケージを持つ変数に対しインライン`inline`を指定することで、複数の翻訳単位で同じ変数を宣言できるようになり、変数の実体はただ一つとすることができる。
外部リンケージを持つ変数に対しインライン`inline`を指定することで、複数の翻訳単位で同じ変数を定義できるようになり、変数の実体はただ一つとすることができる。

C++14までは関数のみインライン指定ができたが、C++17では関数、変数ともにインライン指定が可能になった。

Expand Down Expand Up @@ -34,29 +34,30 @@ int X::foo;

## 仕様

`inline`は関数または変数の宣言、定義に対して指定できる。外部リンケージを持つ関数、変数がどこか一つの翻訳単位で`inline`指定された場合、全ての翻訳単位で`inline`指定されたことになる。`inline`指定された関数、変数は全ての翻訳単位で同じアドレスに配置される。
`inline`は関数または変数の宣言、定義に対して指定できる。外部リンケージを持つ関数、変数がどこか一つの翻訳単位で`inline`指定された場合、同名の変数が宣言されている全ての翻訳単位で`inline`指定を行う必要がある。`inline`指定された関数、変数は全ての翻訳単位で同じアドレスに配置される。

`inline`ではない変数宣言のあとに`inline`は宣言できず、文法違反となる
変数定義のあとに後付けで初めての`inline`指定を行うのは不適格である

```cpp
int a = 100;

// コンパイルエラー
// 不適格につきコンパイルエラー
extern inline int a;
```

`inline`指定した変数は全て同じ定義にすべきである。翻訳単位によって異なる型、初期値で変数を定義したり、`inline`指定を宣言した翻訳単位が出てくる前に別の翻訳単位で同じ変数の定義をしたりすると、プログラムは正常に動作しない
`inline`指定した同名の変数は全て同じ定義にすべきである。翻訳単位によって異なる型、初期値で変数を定義すると、プログラムは正常に動作しない可能性がある (不適格(例外付き診断不要))

```cpp
// b.cpp
int hoge = 10;
inline int hoge = 10;

// a.cpp
inline int hoge = 100;

int main()
{
// コンパイルエラーにならないかもしれないが、正常に動作しない
// 不適格(診断不要)
// コンパイルエラーにならないかもしれないが、正常に動作しない可能性がある
return hoge;
}
```
Expand Down Expand Up @@ -100,31 +101,50 @@ function-specifier:
#include <iostream>

namespace N {
// static指定(翻訳単位ごとにアドレスが変わる)
static inline int static_var = 10;
static inline int static_func(void) {

// 外部リンケージ & 非インライン
// →ODR違反により不適格
/*
int var = 0;
int func() {
return 0;
}
*/

// 外部リンケージ & インライン(inline指定)
// →全翻訳単位でアドレスは同一
inline int inline_var = 10;
inline int inline_func() {
return 20;
}

// inline指定(全翻訳単位でアドレスは同一)
inline int inline_var = 30;
inline int inline_func(void) {
// 内部リンケージ(static指定) & 非インライン
// →翻訳単位毎に異なるアドレス
static int static_var = 30;
static int static_func() {
return 40;
}

// constexprだがinlineは指定されない
constexpr int constexpr_var = 50;
// 関数かつconstexprなので
// 暗黙のうちにinlineが指定される
constexpr int constexpr_func(void) {
// 内部リンケージ(static指定) & インライン(inline指定)
// →外部リンケージではないのでインライン指定はアドレスに影響しない。
// static のみ指定したときと同様に、翻訳単位毎に異なるアドレスになる。
static inline int static_inline_var = 50;
static inline int static_inline_func() {
return 60;
}

// 外部リンケージ & 非インライン(constexpr変数は暗黙にinlineにはならない)
constexpr int constexpr_var = 70;
// 外部リンケージ & インライン(constexpr関数は暗黙にinlineとなる)
constexpr int constexpr_func() {
return 80;
}
}

struct A {
// inline指定(全翻訳単位でアドレスは同一)
static inline int inline_var = 100;
static inline int inline_func(void) {
static inline int inline_func() {
return 200;
}

Expand All @@ -133,12 +153,12 @@ struct A {
static constexpr int constexpr_var = 300;
// 関数かつconstexprなので
// 暗黙のうちにinlineが指定される
static constexpr int constexpr_func(void) {
static constexpr int constexpr_func() {
return 400;
}
};

void func(void);
void func();


//inline_variable1.cpp
Expand All @@ -149,17 +169,19 @@ void func(void);
int main()
{
std::cout << __func__ << std::endl
<< " N::static_var :" << &N::static_var << std::endl
<< " N::static_func :" << reinterpret_cast<void *>(N::static_func) << std::endl
<< " N::inline_var :" << &N::inline_var << std::endl
<< " N::inline_func :" << reinterpret_cast<void *>(N::inline_func) << std::endl
<< " N::constexpr_var :" << &N::constexpr_var << std::endl
<< " N::constexpr_func:" << reinterpret_cast<void *>(N::constexpr_func) << std::endl
<< " N::inline_var :" << &N::inline_var << std::endl
<< " N::inline_func :" << reinterpret_cast<void *>(N::inline_func) << std::endl
<< " N::static_var :" << &N::static_var << std::endl
<< " N::static_func :" << reinterpret_cast<void *>(N::static_func) << std::endl
<< " N::static_inline_var :" << &N::static_inline_var << std::endl
<< " N::static_inline_func:" << reinterpret_cast<void *>(N::static_inline_func) << std::endl
<< " N::constexpr_var :" << &N::constexpr_var << std::endl
<< " N::constexpr_func :" << reinterpret_cast<void *>(N::constexpr_func) << std::endl
<< std::endl
<< " A::inline_var :" << &A::inline_var << std::endl
<< " A::inline_func :" << reinterpret_cast<void *>(A::inline_func) << std::endl
<< " A::constexpr_var :" << &A::constexpr_var << std::endl
<< " A::constexpr_func:" << reinterpret_cast<void *>(A::constexpr_func) << std::endl
<< " A::inline_var :" << &A::inline_var << std::endl
<< " A::inline_func :" << reinterpret_cast<void *>(A::inline_func) << std::endl
<< " A::constexpr_var :" << &A::constexpr_var << std::endl
<< " A::constexpr_func :" << reinterpret_cast<void *>(A::constexpr_func) << std::endl
<< std::endl;

func();
Expand All @@ -173,54 +195,61 @@ int main()
#include <iostream>
#include "inline_variable.hpp"

void func(void)
void func()
{
std::cout << __func__ << std::endl
<< " N::static_var :" << &N::static_var << std::endl
<< " N::static_func :" << reinterpret_cast<void *>(N::static_func) << std::endl
<< " N::inline_var :" << &N::inline_var << std::endl
<< " N::inline_func :" << reinterpret_cast<void *>(N::inline_func) << std::endl
<< " N::constexpr_var :" << &N::constexpr_var << std::endl
<< " N::constexpr_func:" << reinterpret_cast<void *>(N::constexpr_func) << std::endl
<< " N::inline_var :" << &N::inline_var << std::endl
<< " N::inline_func :" << reinterpret_cast<void *>(N::inline_func) << std::endl
<< " N::static_var :" << &N::static_var << std::endl
<< " N::static_func :" << reinterpret_cast<void *>(N::static_func) << std::endl
<< " N::static_inline_var :" << &N::static_inline_var << std::endl
<< " N::static_inline_func:" << reinterpret_cast<void *>(N::static_inline_func) << std::endl
<< " N::constexpr_var :" << &N::constexpr_var << std::endl
<< " N::constexpr_func :" << reinterpret_cast<void *>(N::constexpr_func) << std::endl
<< std::endl
<< " A::inline_var :" << &A::inline_var << std::endl
<< " A::inline_func :" << reinterpret_cast<void *>(A::inline_func) << std::endl
<< " A::constexpr_var :" << &A::constexpr_var << std::endl
<< " A::constexpr_func:" << reinterpret_cast<void *>(A::constexpr_func) << std::endl
<< " A::inline_var :" << &A::inline_var << std::endl
<< " A::inline_func :" << reinterpret_cast<void *>(A::inline_func) << std::endl
<< " A::constexpr_var :" << &A::constexpr_var << std::endl
<< " A::constexpr_func :" << reinterpret_cast<void *>(A::constexpr_func) << std::endl
<< std::endl;
}
```

### 出力

clang++ 5.0.0 にて amd64 向けにコンパイル、実行した場合。
clang++ 14.0.0 (Fedora 14.0.0-1.fc36) にて amd64 向けにコンパイル、実行した場合。

```
main
N::static_var :0x602060
N::static_func :0x400b30
N::inline_var :0x602064
N::inline_func :0x400b40
N::constexpr_var :0x400f6c
N::constexpr_func:0x400b50

A::inline_var :0x602068
A::inline_func :0x400b60
A::constexpr_var :0x400f70
A::constexpr_func:0x400b70
N::inline_var :0x404054
N::inline_func :0x401550
N::static_var :0x404058
N::static_func :0x401530
N::static_inline_var :0x40405c
N::static_inline_func:0x401540
N::constexpr_var :0x402144
N::constexpr_func :0x401560

A::inline_var :0x404060
A::inline_func :0x401570
A::constexpr_var :0x402148
A::constexpr_func :0x401580

func
N::static_var :0x60206c
N::static_func :0x400e00
N::inline_var :0x602064
N::inline_func :0x400b40
N::constexpr_var :0x400f7c
N::constexpr_func:0x400b50

A::inline_var :0x602068
A::inline_func :0x400b60
A::constexpr_var :0x400f70
A::constexpr_func:0x400b70
N::inline_var :0x404054
N::inline_func :0x401550
N::static_var :0x404064
N::static_func :0x401870
N::static_inline_var :0x404068
N::static_inline_func:0x401880
N::constexpr_var :0x402154
N::constexpr_func :0x401560

A::inline_var :0x404060
A::inline_func :0x401570
A::constexpr_var :0x402148
A::constexpr_func :0x401580

```

表示されるアドレスは環境によって異なる可能性がある。
Expand All @@ -229,4 +258,9 @@ func
- [C++11 constexpr](/lang/cpp11/constexpr.md)

## 参照
- [P0386R2 Inline Variables](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r2.pdf)
- [N4147 - Inline variables, or encapsulated expressions?](https://isocpp.org/files/papers/n4147.pdf), 2014-09-25
- [N4424 - Inline Variables](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4424.pdf), 2015-04-07
- [P0386R0 - Inline Variables](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r0.pdf), 2016-05-30
- [P0386R2 - Inline Variables](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r2.pdf), 2016-06-24
- [P0607R0 - Inline Variables for the Standard Library](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0607r0.html), 2017-02-27
- [C++1z インライン変数 - Faith and Brave - C++で遊ぼう](https://faithandbrave.hateblo.jp/entry/2016/11/11/172954)