サーチ…
備考
コンストラクトの動作が指定されていない場合には、標準では、動作にいくつかの制約を課すが、与えられた状況で何が起こるか文書化する必要はありません実装にいくつかの自由を残します。これは、 実装が定義された動作と、実装が何が起こるかを記録することが要求される場合と、何も起こり得ない未定義の動作とは対照的です。
TU全体のグローバルの初期化の順序
翻訳単位内では、グローバル変数の初期化の順序が指定されていますが、翻訳単位の初期化の順序は不特定です。
次のファイルを持つプログラム
foo.cpp
#include <iostream> int dummyFoo = ((std::cout << "foo"), 0);
bar.cpp
#include <iostream> int dummyBar = ((std::cout << "bar"), 0);
main.cpp
int main() {}
出力として生成される可能性があります:
foobar
または
barfoo
スタティック・イニシアチブ・オーダー・フィアスコにつながる可能性があります。
範囲外列挙型の値
スコープ付き列挙型が値を保持するには小さすぎる整数型に変換された場合、結果の値は指定されません。例:
enum class E {
X = 1,
Y = 1000,
};
// assume 1000 does not fit into a char
char c1 = static_cast<char>(E::X); // c1 is 1
char c2 = static_cast<char>(E::Y); // c2 has an unspecified value
また、整数が列挙型に変換され、整数の値が列挙型の値の範囲外にある場合、結果の値は指定されません。例:
enum Color {
RED = 1,
GREEN = 2,
BLUE = 3,
};
Color c = static_cast<Color>(4);
ただし、次の例では、ソース値がすべての列挙子と等しくないにもかかわらず、列挙型の範囲内にあるため、振る舞いは未定義ではありません 。
enum Scale {
ONE = 1,
TWO = 2,
FOUR = 4,
};
Scale s = static_cast<Scale>(3);
ここでs
値3を持っている、との不平等になるONE
、 TWO
、およびFOUR
。
偽のvoid * valueからの静的キャスト
場合void*
値は、タイプ、オブジェクトへのポインタに変換され、 T*
、しかし適切に位置合わせされていないT
、得られたポインタ値は不定です。例:
// Suppose that alignof(int) is 4
int x = 42;
void* p1 = &x;
// Do some pointer arithmetic...
void* p2 = static_cast<char*>(p1) + 2;
int* p3 = static_cast<int*>(p2);
p2
はint
型のオブジェクトを指すことができないため、 p3
の値は未定義です。その値は正しく整列されたアドレスではありません。
いくつかのreinterpret_cast変換の結果
1つの関数ポインタ型から別の関数型へのreinterpret_cast、またはある関数参照型から別の関数型へのreinterpret_cast
の結果は指定されていません。例:
int f();
auto fp = reinterpret_cast<int(*)(int)>(&f); // fp has unspecified value
1つのオブジェクトポインタ型から別のオブジェクト型へのreinterpret_cast
の結果、またはあるオブジェクト参照型から別のオブジェクト参照型へのreinterpret_cast
の結果は指定されていません。例:
int x = 42;
char* p = reinterpret_cast<char*>(&x); // p has unspecified value
しかし、ほとんどのコンパイラではstatic_cast<char*>(static_cast<void*>(&x))
結果ポインタp
がx
最初のバイトを指していました。これはC ++ 11の標準的な動作です。詳細については、 型変換を参照してください。
いくつかのポインタ比較の結果
<
、 >
、 <=
、または>=
を使用して2つのポインタを比較すると、結果は次の場合には指定されません。
ポインターは異なる配列を指します。 (非配列オブジェクトはサイズ1の配列と見なされます)
int x; int y; const bool b1 = &x < &y; // unspecified int a[10]; const bool b2 = &a[0] < &a[1]; // true const bool b3 = &a[0] < &x; // unspecified const bool b4 = (a + 9) < (a + 10); // true // note: a+10 points past the end of the array
ポインターは同じオブジェクトを指しますが、アクセス制御の異なるメンバーにポイントします。
class A { public: int x; int y; bool f1() { return &x < &y; } // true; x comes before y bool f2() { return &x < &z; } // unspecified private: int z; };
リファレンスが占めるスペース
参照はオブジェクトではなく、オブジェクトとは異なり、連続したいくつかのメモリバイトを占有することは保証されていません。標準では、リファレンスがストレージを必要とするかどうかは不明です。言語の多くの機能は、参照が占める可能性のある記憶域を移植可能に調べることを不可能にしています。
-
sizeof
が参照に適用される場合、参照される型のサイズが返されるため、参照が格納領域を占有しているかどうかに関する情報は提供されません。 - 参照の配列は不正です。したがって、配列の仮想参照の2つの連続した要素のアドレスを調べて、参照のサイズを判断することはできません。
- 参照のアドレスが取られた場合、結果は参照先のアドレスになるので、参照自体へのポインタを取得することはできません。
- クラスに参照メンバがある場合、
offsetof
を使用してそのメンバのアドレスを抽出しようとすると、標準レイアウトクラスではないため、未定義の動作が発生します。 - クラスに参照メンバーがある場合、そのクラスはもはや標準レイアウトではないため、参照を格納するために使用されるデータにアクセスしようとすると、未定義または未指定の動作が発生します。
実際には、場合によっては、ポインタ変数と同様に参照変数を実装することができ、したがってポインタと同じ量の記憶域を占有することがありますが、参照が最適化できるため、参照は全くスペースを占有しません。たとえば、次のようになります。
void f() {
int x;
int& r = x;
// do something with r
}
コンパイラは、単に治療に自由であるr
別名としてx
とのすべての出現置き換えるr
、関数の残りの部分でf
とx
、および保持するために、任意のストレージ割り当てませr
。
関数の引数の評価順序
関数が複数の引数を持つ場合、評価される順序は不特定です。次のコードはx = 1, y = 2
またはx = 2, y = 1
を出力できますが、
int f(int x, int y) {
printf("x = %d, y = %d\n", x, y);
}
int get_val() {
static int x = 0;
return ++x;
}
int main() {
f(get_val(), get_val());
}
C ++ 17では、関数の引数の評価の順序は不明のままです。
しかし、各関数の引数は完全に評価され、呼び出し元のオブジェクトは関数の引数がある前に評価されます。
struct from_int {
from_int(int x) { std::cout << "from_int (" << x << ")\n"; }
};
int make_int(int x){ std::cout << "make_int (" << x << ")\n"; return x; }
void foo(from_int a, from_int b) {
}
void bar(from_int a, from_int b) {
}
auto which_func(bool b){
std::cout << b?"foo":"bar" << "\n";
return b?foo:bar;
}
int main(int argc, char const*const* argv) {
which_func( true )( make_int(1), make_int(2) );
}
これは印刷する必要があります:
bar
make_int(1)
from_int(1)
make_int(2)
from_int(2)
または
bar
make_int(2)
from_int(2)
make_int(1)
from_int(1)
make
またはfrom
のいずれかの後にbar
が表示されず、印刷されないことがあります:
bar
make_int(2)
make_int(1)
from_int(2)
from_int(1)
または類似。 make_int
の後にC ++ 17の印刷bar
の前には、 from_int
を実行する前にmake_int
の両方をmake_int
ていたのと同じように、
最も標準的なライブラリクラスの移動状態
すべての標準ライブラリコンテナは、移動後に有効であるが不特定の状態のままである。たとえば、次のコードでは、 v2
には移動後に{1, 2, 3, 4}
が含まれますが、 v1
は必ず空であるとは限りません。
int main() {
std::vector<int> v1{1, 2, 3, 4};
std::vector<int> v2 = std::move(v1);
}
クラスによっては、move-from状態が正確に定義されているものがあります。最も重要なケースは、 std::unique_ptr<T>
場合です。これは、移動後にnullになることが保証されています。