厳密なポインターエイリアシング:特定の問題の解決策はありますか?
-
24-10-2019 - |
質問
厳格なポインターエイリアシングルールを破ることによって引き起こされる問題があります。タイプがあります T
これは、テンプレートといくつかの積分タイプに由来します Int
同じサイズ( sizeof
)。私のコードは基本的に次のことを行います。
T x = some_other_t;
if (*reinterpret_cast <Int*> (&x) == 0)
...
なぜなら T
コンストラクターを持つことができる人工(サイズ制限以外)タイプですが、私はの組合を作ることができません T
と Int
. 。 (これはC ++ 0xのみでのみ許可されており、まだGCCでサポートされていません)。
上記の擬似コードを書き直して機能を維持し、厳格なエイリアシングルールを破ることを避ける方法はありますか?これはテンプレートであることに注意してください、私は制御できません T
またはの値 some_other_t
;割り当てとその後の比較は、テンプレートコード内で発生します。
(記録のために、上記のコードはGCC 4.5で破損し始めた場合 T
ビットフィールドが含まれています。)
解決
static inline int is_T_0(const T *ob)
{
int p;
memcpy(&p, ob, sizeof(int));
return p == 0;
}
void myfunc(void)
{
T x = some_other_t;
if (is_T_0(&x))
...
私のシステムでは、GCCは両方を最適化します is_T_0()
と memcpy()
, 、わずか数回のアセンブリの指示があります myfunc()
.
他のヒント
聞いたことがありますか boost::optional
?
私はここでの本当の問題については不明であることを認めなければなりません...しかし、Boost ::オプションの許可は価値によって保存されますが、実際のメモリが初期化されたかどうかを知っています。私はまた、建設と破壊を確保することを許可しているので、私が推測するのに適している可能性があります。
編集:
私は最終的に問題を把握したと思います:あなたはメモリのさまざまなポイントで多くのオブジェクトを割り当てることができるようにしたいと思います、そして、あなたはこの時点でメモリが本当にオブジェクトを持っているかどうかを知りたいです。
残念ながら、あなたのソリューションには大きな問題があります。それは間違っています。もしあれば T
どういうわけかaで表すことができます null
ビットパターン、そうすれば、それは統合されたメモリだと思うでしょう。
少なくとも1つの情報を追加するには、自分自身に頼る必要があります。それは実際にはそれほどではありませんが、結局のところ、それは成長のわずか3%(4バイトで33ビット)です。
たとえば、いくつかのMimickを使用できます boost::optional
しかし、配列の方法で(パディングの損失を避けるため)。
template <class T, size_t N>
class OptionalArray
{
public:
private:
typedef unsigned char byte;
byte mIndex[N/8+1];
byte mData[sizeof(T)*N]; // note: alignment not considered
};
それからそれはそれと同じくらい簡単です:
template <class T, size_t N>
bool OptionalArray<T,N>::null(size_t const i) const
{
return mIndex[i/8] & (1 << (i%8));
}
template <class T, size_t N>
T& OptionalArray<T,N>::operator[](size_t const i)
{
assert(!this->null(i));
return *reinterpret_cast<T*>(mData[sizeof(T)*i]);
}
ノート: :簡単にするために、私はアラインメントの問題を考慮していません。主題について知らない場合は、記憶をいじる前にそれについて読んでください:)
これはどう:
Int zero = 0;
T x = some_other_t;
if (std::memcmp(&x, &zero, sizeof(zero)) == 0)
それほど効率的ではないかもしれませんが、警告を取り除くはずです。
補遺#1:
以来 T
と同じサイズになるように制約されています Int
, 、タイプのダミービットワイズゼロ値にします T
そして、それに対して直接比較します(アガイストをキャストして比較する代わりに Int(0)
).
あなたのプログラムがシングルスレッドされている場合、あなたは次のようなものを持つことができます:
template <typename T>
class Container
{
public:
void foo(T val)
{
if (zero_ == val)
{
// Do something
}
}
private:
struct Zero
{
Zero() {memset(&val, 0, sizeof(val));}
bool operator==(const T& rhs) const {return val == rhs;}
T val;
};
static Zero zero_;
};
マルチスレッドの場合は、静的メンバーの使用を避ける必要があります zero_
, 、そして各コンテナインスタンスにそれを保持してもらう zero_
メンバー:
template <typename T>
class MTContainer
{
public:
MTContainer() {memset(zero_, 0, sizeof(zero_));}
void foo(T val)
{
if (val == zero_)
{
// Do something
}
}
private:
T zero_;
};
補遺#2:
上記の補遺を別の、より簡単な方法に入れましょう:
// zero is a member variable and is inialized in the container's constructor
T zero;
std::memset(&zero, 0, sizeof(zero));
T x = some_other_t;
if (x == zero)
単純にしないのはなぜですか:
const Int zero = 0;
if (memcmp(&some_other_t, &zero, sizeof(zero)) == 0)
/* some_other_t is 0 */
(あなたも追加しようとするかもしれません static
予選 zero
パフォーマンスの違いがあるかどうかを確認するために)
33ビットコンピューターを使用します。 ; P
それはハックのように感じますが、どうやら私は解決策を見つけたようです: volatile
為に Int
鋳造。基本的に、私が今やっているのは次のとおりです。
T x = some_other_t;
if (*reinterpret_cast <volatile Int*> (&x) == 0)
...
ビットフィールドの問題 T
今はなくなっています。それでも、私はこれについてあまり満足していません volatile
C ++ afaikで明確に定義されていません...