(これは、未定義の動作 (UB) に関する別の質問です。このコードが何らかのコンパイラで「機能する」場合、それは UB の土地では何の意味もありません。それは理解されています。しかし、正確には、以下のどの行で UB に交差するのでしょうか?)
(SO には非常によく似た質問が既にいくつかあります。たとえば、(1) ですが、ポインターを逆参照する前に、ポインターを安全に使用できることに興味があります。)
非常に単純な基本クラスから始めます。virtual
メソッドはありません。継承なし。(おそらく、これは POD であるすべてのものに拡張できますか?)
struct Base {
int first;
double second;
};
次に、(非virtual
)メソッドを追加し、メンバーを追加しない単純な拡張。virtual
継承なし。
struct Derived : public Base {
int foo() { return first; }
int bar() { return second; }
};
次に、次の行を検討してください。定義された動作からの逸脱がある場合、正確にどの行を知りたいと思います。私の推測では、ポインターに対して多くの計算を安全に実行できると思います。これらのポインター計算の一部は、完全に定義されていない場合でも、少なくとも完全に役に立たないわけではない「不確定/未指定/実装定義」の値を与える可能性はありますか?
void foo () {
Base b;
void * vp = &b; // (1) Defined behaviour?
cout << vp << endl; // (2) I hope this isn't a 'trap value'
cout << &b << endl; // (3a) Prints the same as the last line?
// (3b) It has the 'same value' in some sense?
Derived *dp = (Derived*)(vp);
// (4) Maybe this is an 'indeterminate value',
// but not fully UB?
cout << dp << endl; // (5) Defined behaviour also? Should print the same value as &b
編集: プログラムがここで終了した場合、それは UB でしょうか? この段階では、ポインター自体を出力に出力する以外に、 で何もしようとしていないことに注意してください。dp
単にキャスティングがUBなら、質問はここで終わると思います。
// I hope the dp pointer still has a value,
// even if we can't dereference it
if(dp == &b) { // (6) True?
cout << "They have the same value. (Whatever that means!)" << endl;
}
cout << &(b.second) << endl; (7) this is definitely OK
cout << &(dp->second) << endl; // (8) Just taking the address. Is this OK?
if( &(dp->second) == &(b.second) ) { // (9) True?
cout << "The members are stored in the same place?" << endl;
}
}
上記の(4)については少し神経質です。しかし、void ポインターとの間でキャストすることは常に安全だと思います。おそらく、そのようなポインタの値について議論することができます。しかし、キャストを行い、ポインタを出力するように定義されていますcout
か?
(6)も重要です。これは true と評価されますか?
(8)では、このポインターが初めて逆参照されます (正しい用語?)。ただし、この行は から読み取らないことに注意してくださいdp->second
。これはまだ単なる左辺値であり、そのアドレスを取得します。このアドレスの計算は、C 言語から得た単純なポインター算術規則によって定義されていると思います。
上記のすべてに問題がなければ、それが問題ないことを証明できstatic_cast<Derived&>(b)
、完全に使用可能なオブジェクトにつながる可能性があります。