名前空間
変種
操作

as-if ルール

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

プログラムの観察可能な動作を変更しない、あらゆるコード変換を許可します。

目次

[編集] 説明

プログラムの観察可能な動作には以下が含まれます。

  • すべてのシーケンスポイントにおいて、すべてのvolatileオブジェクトの値は安定しています(以前の評価は完了し、新しい評価は開始されていません)。
(C++11まで)
  • volatileオブジェクトへのアクセス(読み取りと書き込み)は、それらが現れる式のセマンティクスに厳密に従って発生します。特に、同じスレッド上の他のvolatileアクセスと比較して再順序付けされません
(C++11以降)
  • プログラム終了時、ファイルに書き込まれたデータは、あたかもプログラムが記述どおりに実行されたかのように正確です。
(C++26まで)
  • ホスト環境に渡されるデータはファイルに書き込まれます。
(C++26以降)
  • 対話型デバイスに送信されるプロンプトテキストは、プログラムが入力待ちになる前に表示されます。
  • ISO C pragma #pragma STDC FENV_ACCESS がサポートされており、ONに設定されている場合、浮動小数点環境(浮動小数点例外と丸めモード)への変更は、あたかも記述どおりに実行されたかのように浮動小数点演算子と関数呼び出しによって観察されることが保証されます。ただし、以下の場合は例外です。
    • キャストと代入以外の任意の浮動小数点式の結果は、その式の型とは異なる浮動小数点型の範囲と精度を持つことがあります(FLT_EVAL_METHODを参照)。
    • 上記にもかかわらず、任意の浮動小数点式の中間結果は、無限の範囲と精度であるかのように計算されることがあります( #pragma STDC FP_CONTRACTOFF でない限り)。

C++コンパイラは、同じ入力が与えられた場合、プログラムの観察可能な動作がその入力に対応する可能な観察可能な動作の1つである限り、プログラムに対して任意の変更を実行することが許可されます。

ただし、特定の入力が未定義動作を引き起こす場合、コンパイラはその入力によるプログラムの観察可能な動作を保証できません。たとえ観察可能な動作のいずれかの操作が、可能な未定義の操作の前に発生したとしてもです。

(C++26まで)

プログラムは観察可能なチェックポイントを含むことがあります。

操作OPは、すべての未定義操作Uに対して、OPCPの前に発生し、CPUの前に発生するような観察可能なチェックポイントCPがある場合、未定義フリーです。特定の入力を持つプログラムの定義済みプレフィックスは、そのすべての未定義フリー操作で構成されます。

C++コンパイラは、同じ入力が与えられた場合、プログラムの定義済みプレフィックスの観察可能な動作が、その定義済みプレフィックスに対応する可能な観察可能な動作の1つである限り、プログラムに対して任意の変更を実行することが許可されます。

特定の入力が未定義動作を引き起こす場合、コンパイラはその定義済みプレフィックスに属さない、その入力によるプログラムの観察可能な動作を保証できません。

(C++26以降)

[編集] 注記

コンパイラは(通常)外部ライブラリのコードを分析してI/Oまたはvolatileアクセスを実行するかどうかを判断できないため、サードパーティライブラリの呼び出しも最適化の影響を受けません。ただし、標準ライブラリの呼び出しは、最適化中に他の呼び出しに置き換えられたり、削除されたり、プログラムに追加されたりすることがあります。静的リンクされたサードパーティライブラリのコードは、リンク時最適化の対象となることがあります。

未定義動作を含むプログラムは、異なる最適化設定で再コンパイルすると、観察可能な動作がしばしば変化します。例えば、符号付き整数オーバーフローのテストがそのオーバーフローの結果に依存している場合、例えばif (n + 1 < n) abort();のように、一部のコンパイラでは完全に削除されます。これは符号付きオーバーフローが未定義動作であるため、最適化ツールはそれが決して起こらないと仮定してもよく、テストは冗長であると見なせるからです。

コピー省略はas-ifルールの例外です。コンパイラは、一時オブジェクトの移動コンストラクタとコピーコンストラクタへの呼び出し、および対応するデストラクタへの呼び出しが観察可能な副作用を持つ場合でも、それらを削除することがあります。

newにはas-ifルールのもう一つの例外があります。コンパイラは、ユーザー定義の置き換えが提供され、観察可能な副作用を持つ場合でも、置き換え可能な割り当て関数への呼び出しを削除することがあります。

(C++14以降)

浮動小数点例外の数と順序は、次の浮動小数点操作によって観察される状態が最適化が行われなかった場合と同様である限り、最適化によって変更されることがあります。

#pragma STDC FENV_ACCESS ON
for (i = 0; i < n; ++i)
    x + 1; // x + 1 is dead code, but may raise FP exceptions
           // (unless the optimizer can prove otherwise). However, executing it n times
           // will raise the same exception over and over. So this can be optimized to:
if (0 < n)
    x + 1;

[編集]

int& preinc(int& n) { return ++n; }
int add(int n, int m) { return n + m; }
 
// volatile input to prevent constant folding
volatile int input = 7;
 
// volatile output to make the result a visible side-effect
volatile int result;
 
int main()
{
    int n = input;
// using built-in operators would invoke undefined behavior
//  int m = ++n + ++n;
// but using functions makes sure the code executes as-if 
// the functions were not overlapped
    int m = add(preinc(n), preinc(n));
    result = m;
}

出力

# full code of the main() function as produced by the GCC compiler
# x86 (Intel) platform:
        movl    input(%rip), %eax   # eax = input
        leal    3(%rax,%rax), %eax  # eax = 3 + eax + eax
        movl    %eax, result(%rip)  # result = eax
        xorl    %eax, %eax          # eax = 0 (the return value of main())
        ret
 
# PowerPC (IBM) platform:
        lwz 9,LC..1(2)
        li 3,0          # r3 = 0 (the return value of main())
        lwz 11,0(9)     # r11 = input;
        slwi 11,11,1    # r11 = r11 << 1;
        addi 0,11,3     # r0 = r11 + 3;
        stw 0,4(9)      # result = r0;
        blr
 
# Sparc (Sun) platform:
        sethi   %hi(result), %g2
        sethi   %hi(input), %g1
        mov     0, %o0                 # o0 = 0 (the return value of main)
        ld      [%g1+%lo(input)], %g1  # g1 = input
        add     %g1, %g1, %g1          # g1 = g1 + g1
        add     %g1, 3, %g1            # g1 = 3 + g1
        st      %g1, [%g2+%lo(result)] # result = g1
        jmp     %o7+8
        nop
 
# in all cases, the side effects of preinc() were eliminated, and the
# entire main() function was reduced to the equivalent of result = 2 * input + 3;

[編集] 関連項目

as-if ruleCドキュメント
English 日本語 中文(简体) 中文(繁體)