Skip to content

Commit

Permalink
未初期化領域への暗黙的なオブジェクト構築 : 仕様を追記
Browse files Browse the repository at this point in the history
  • Loading branch information
onihusube committed Feb 21, 2023
1 parent d68f5d6 commit 79d7e85
Showing 1 changed file with 31 additions and 6 deletions.
Expand Up @@ -23,7 +23,7 @@ X *make_x() {
}
```
この様なコードは純粋なC++コードでは`new`式を用いることになるため、その場合は問題ない。
この様なコードは純粋なC++では`new`式を用いることになり、その場合は問題ない。
```cpp
// new式を使用する場合
Expand Down Expand Up @@ -75,7 +75,7 @@ float pun(int n) {
// U::nの生存期間が開始
U u = {.n = n};

// このコピーではunオブジェクト表現がコピーされるものの、メンバの生存期間は開始されない
// このコピーではuのオブジェクト表現がコピーされるものの、メンバの生存期間は開始されない
// そのため、アクティブメンバが無い状態となる
U u2 = u;

Expand Down Expand Up @@ -164,13 +164,13 @@ C++においては、ポインタに対する演算(`+ -`など)はそのポ
### オブジェクトを暗黙的に構築する操作
標準ライブラリ中の次の関数は、*implicit-lifetime types*に対して使用した場合に、その振る舞いの一環としてそのオブジェクトを暗黙的に構築する
標準ライブラリ中の次の関数は、*implicit-lifetime types*に対して使用した場合に、その振る舞いの一環として指定された領域内にそのオブジェクトを暗黙的に構築する
- `new`演算子
- `opreator new`
- `opreator new[]`
- アロケータ
- [`std::allocator::allocate`](/reference/memory/allocator/allocate.md)
- [`std::allocator<T>::allocate`](/reference/memory/allocator/allocate.md)
- Cライブラリ関数
- `std::aligned_alloc`
- `std::calloc`
Expand All @@ -193,8 +193,33 @@ C++においては、ポインタに対する演算(`+ -`など)はそのポ
- ネストしたものも含めて、結果領域に暗黙にオブジェクトを構築する
- ネストしたもの、とは例えばクラスメンバや配列の要素のこと
### デストラクタ呼び出しによる生存期間の終了
(執筆中)
また、これらの操作に限らず、`char, unsigned char, std::byte`の配列オブジェクトを構築しその生存期間を開始する操作は、その配列オブジェクトが占有する領域内にその要素のオブジェクトを暗黙的に構築する。
### 暗黙的なオブジェクト構築
オブジェクトを暗黙的に構築する操作では、そうすることでプログラムが定義された振る舞いをするようになる(すなわち、未定義動作が回避できる)場合に、*implicit-lifetime types*の0個以上のオブジェクトを暗黙的に構築しその生存期間を開始させる。そのような、暗黙的なオブジェクト構築によってプログラムに定義された振る舞いをもたらすオブジェクトが1つも存在しない場合は未定義動作となる(これは今まで通り)。逆に、そのようなオブジェクトが複数存在している場合は、どのオブジェクトが暗黙的に構築されるかは未規定(これは、都度適切なオブジェクトが選択され構築されることを意図している)。
暗黙的なオブジェクト構築が行われる場合、そのサブオブジェクトの*implicit-lifetime types*のオブジェクトも暗黙的に構築され生存期間が開始されるが、*implicit-lifetime types*ではないオブジェクトは暗黙的に構築されないため明示的な初期化が必要となる。
暗黙的なオブジェクト構築を行わなくてもプログラムが定義された振る舞いをする(未定義動作とならない)場合、対象の操作は暗黙的にオブジェクトを構築せず、通常の効果のみをもたらす。
さらに、オブジェクトを暗黙的に構築する操作の内メモリを確保する関数等一部の操作では、暗黙的なオブジェクト構築によって作成されたオブジェクトを指す適切なポインタを返すことが規定される。これらの操作においては、そのポインタ値を返すことでプログラムが定義された振る舞いをするようになる場合に、指定された領域の先頭アドレスをアドレスとする暗黙的に構築されたオブジェクトの1つを選択し、そのオブジェクトを指すポインタ値を生成しそれを返す。そのような、プログラムに定義された振る舞いをもたらすようなポインタ値が存在しない場合は未定義動作となる。逆に、そのようなポインタ値が複数存在している場合は、どのポインタ値が生成されるかは未規定。
そのようなポインタ生成を行わなくてもプログラムが定義された振る舞いをする場合、そのようなポインタ値は生成されず、対象の操作は通常のポインタ値を返す。
### 擬似デストラクタ呼び出しによる生存期間の終了
一部の文脈では、スカラー型のオブジェクトに対してデストラクタ呼び出しを行うことができ、その場合のデストラクタ呼び出しのことを擬似デストラクタ呼び出し(*pseudo-destructor call*)と呼ぶ。
C++17まで、擬似デストラクタ呼び出しには何の効果もなかった(テンプレートの文脈でクラス型との構文上の互換性を取るためのものでしかなかった)が、C++20からは擬似デストラクタ呼び出しはそのスカラー型オブジェクトの生存期間を終了させることが規定される。
これに伴い、[`destroy_at()`](/reference/memory/destroy_at.md)などの擬似デストラクタ呼び出しと等価のことを行うライブラリ機能においても、スカラ型のオブジェクトの生存期間を終了させるようになる。
### これらの変更の影響
これらの変更はあくまでオブジェクト生存期間に関する規則を変更しただけに過ぎず、その影響はコンパイラ等の実装のオブジェクト生存期間の認識が変わるだけである。それによって、今まで未定義動作となっていたコードが未定義動作ではなくなり、未定義動作をトリガーとする最適化を受ける可能性が将来にわたって取り除かれることになる。
したがって、これらの変更によって最適化によるもの以外の実行時の振る舞いが変化することはなく、暗黙的なオブジェクト構築は実際にコンストラクタを呼んだり何か初期化を行うものではない。
## 例
(執筆中)
Expand Down

1 comment on commit 79d7e85

@onihusube
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.