たくさんの型を適切な大きさのchar配列にパックし、正しく型付けされた個々の参照としてデータにアクセスできるようにするクラステンプレートを作成しようとしています。現在、標準によれば、これは厳密なエイリアシング違反につながる可能性char[]
があり、互換性のないオブジェクトを介してデータにアクセスしているため、未定義の動作につながる可能性があります。具体的には、標準は次のように述べています。
プログラムが次のタイプのいずれか以外のglvalueを介してオブジェクトの保存された値にアクセスしようとした場合、動作は未定義です。
- オブジェクトの動的タイプ、
- オブジェクトの動的タイプのcv修飾バージョン。
- オブジェクトの動的タイプに類似したタイプ(4.4で定義)、
- オブジェクトの動的型に対応する符号付きまたは符号なしの型である型、
- オブジェクトの動的型のcv修飾バージョンに対応する符号付きまたは符号なし型である型。
- 要素または非静的データメンバー(再帰的に、サブアグリゲートまたは含まれるユニオンの要素または非静的データメンバーを含む)の中に前述のタイプの1つを含む集合体または共用体タイプ。
- オブジェクトの動的型の(おそらくcv修飾された)基本クラス型である型、
char
またはunsigned char
タイプ。
強調表示された箇条書きの文言を考えると、私は次のalias_cast
アイデアを思いつきました。
#include <iostream>
#include <type_traits>
template <typename T>
T alias_cast(void *p) {
typedef typename std::remove_reference<T>::type BaseType;
union UT {
BaseType t;
};
return reinterpret_cast<UT*>(p)->t;
}
template <typename T, typename U>
class Data {
union {
long align_;
char data_[sizeof(T) + sizeof(U)];
};
public:
Data(T t = T(), U u = U()) { first() = t; second() = u; }
T& first() { return alias_cast<T&>(data_); }
U& second() { return alias_cast<U&>(data_ + sizeof(T)); }
};
int main() {
Data<int, unsigned short> test;
test.first() = 0xdead;
test.second() = 0xbeef;
std::cout << test.first() << ", " << test.second() << "\n";
return 0;
}
(上記のテストコード、特にクラスはアイデアの単なるデモンストレーションなので、どのように使用するかData
を指摘しないでください。テンプレートもcv修飾型を処理するように拡張する必要があり、安全にしか使用できません。配置要件が満たされている場合に使用されますが、このスニペットがアイデアを実証するのに十分であることを願っています。)std::pair
std::tuple
alias_cast
このトリックはg++による警告を沈黙させ(でコンパイルした場合g++ -std=c++11 -Wall -Wextra -O2 -fstrict-aliasing -Wstrict-aliasing
)、コードは機能しますが、これは厳密なエイリアシングベースの最適化をスキップするようにコンパイラーに指示する本当に有効な方法ですか?
有効でない場合、エイリアシングルールに違反せずに、このようなchar配列ベースの汎用ストレージクラスを実装するにはどうすればよいでしょうか。
alias_cast
編集:を次のような単純なものに置き換えreinterpret_cast
ます:
T& first() { return reinterpret_cast<T&>(*(data_ + 0)); }
U& second() { return reinterpret_cast<U&>(*(data_ + sizeof(T))); }
g ++でコンパイルすると、次の警告が生成されます。
aliastest-so-1.cpp:'T&Data :: first()[with T=int;のインスタンス化 U = short unsigned int]':aliastest-so-1.cpp:28:16:
ここから必要ですaliastest-so-1.cpp:21:58:警告:型のパンニングされたポインターを逆参照すると、厳密なエイリアスルールが破られます[- Wstrict-aliasing]