C++ でクラス定義が与えられた場合
class A
{
public:
//methods definition
....
private:
int i;
char *str;
....
}
C++ テンプレートのメタプログラミングを使用して、コンパイル時にクラス メンバーのオフセットを計算することは可能ですか? クラスは POD ではなく、仮想メソッド、プリミティブ、およびオブジェクト データ メンバーを持つことができます。
C++ でクラス定義が与えられた場合
class A
{
public:
//methods definition
....
private:
int i;
char *str;
....
}
C++ テンプレートのメタプログラミングを使用して、コンパイル時にクラス メンバーのオフセットを計算することは可能ですか? クラスは POD ではなく、仮想メソッド、プリミティブ、およびオブジェクト データ メンバーを持つことができます。
Matthieu M.の回答に基づいていますが、短く、マクロはありません:
template<typename T, typename U> constexpr size_t offsetOf(U T::*member)
{
return (char*)&((T*)nullptr->*member) - (char*)nullptr;
}
そして、次のように呼び出されます。
struct X { int a, b, c, d; }
std::cout << "offset of c in X == " << offsetOf(&X::c);
編集:
ジェイソン・ライスは正しいです。これは、C++11 では実際の定数式を生成しません。http://en.cppreference.com/w/cpp/language/constant_expressionの制限を考えると、可能ではないようです。特に、ポインターの違いはなくreinterpret_cast
、定数式にすることができます。
ええと...C++ 11では、実際には、通常のC ++機能を使用して(つまり、特定のコンパイラ組み込み関数に委任せずに)そのようなオフセットを正しく計算できます。
ライブワークスペースでの動作:
template <typename T, typename U>
constexpr int func(T const& t, U T::* a) {
return (char const*)&t - (char const*)&(t.*a);
}
t
ただし、これはここでのインスタンスへの参照であることに依存しており、constexpr
すべてのクラスに適用できるとは限りません。ただし、コンストラクターである限り、メソッドやコンストラクターT
を持つことを禁じることはありません。virtual
constexpr
それでも、これはかなりの障害です。評価されていないコンテキストではstd::declval<T>()
、オブジェクトを持つことをシミュレートするために実際に使用できます。何も持っていない間。したがって、これはオブジェクトのコンストラクターに特定の要件を課しません。一方、そのようなコンテキストから抽出できる値はわずかです...そしてそれらは現在のコンパイラでも問題を引き起こします...まあ、それを偽造しましょう!
ライブワークスペースでの動作:
template <typename T, typename U>
constexpr size_t offsetof_impl(T const* t, U T::* a) {
return (char const*)t - (char const*)&(t->*a) >= 0 ?
(char const*)t - (char const*)&(t->*a) :
(char const*)&(t->*a) - (char const*)t;
}
#define offsetof(Type_, Attr_) \
offsetof_impl((Type_ const*)nullptr, &Type_::Attr_)
私が予測する唯一の問題virtual
は、ベースオブジェクトの実行時の配置のため、継承に関するものです。もしあれば、他の欠陥があれば喜んで提示されます。
いいえ、一般的ではありません。
offsetof マクロは POD (plain old data) 構造体用に存在し、C++0x で標準レイアウト構造体 (または他の同様のわずかな拡張) にわずかに拡張される場合があります。したがって、これらの制限されたケースについては、解決策があります。
C++ は、コンパイラ作成者に多くの自由を提供します。一部のクラスがクラスのメンバーへの可変オフセットを持つことを防ぐ句については知りませんが、コンパイラがそれを行う理由もわかりません。;)
ここで、コード標準に準拠しつつもオフセットを維持するための 1 つのアプローチは、offsetof が機能する POD (または一部の C++0x 拡張) サブ構造体にデータを貼り付けてから、そのサブ構造体で作業することです。クラス全体ではなく構造体。または、標準への準拠を放棄することもできます。クラス内の構造体のオフセットはわかりませんが、構造体内のメンバーのオフセットはわかります。
重要な質問は、「なぜこれが必要なのか、そして本当に正当な理由があるのか」ということです。
元の C++ 設計者の 1 人である Stanley B. Lippman によって書かれた 1996 年の本「Inside the C++ Object Model」では、第 4.4 章で Pointer-to-Member Functions に言及しています。
非静的データ メンバーのアドレスを取得して返される値は、クラス レイアウト内のメンバーの位置のバイト値 (プラス 1) です。それは不完全な値と考えることができます。メンバーの実際のインスタンスにアクセスする前に、クラス オブジェクトのアドレスにバインドする必要があります。
前世のどこかで +1 を使ったことを漠然と思い出しましたが、この構文を見たことも使用したこともありません。
class t
{
public:
int i;
int j;
};
int (t::*pmf)() = &t::i;
少なくとも説明によると、これは「ほぼ」オフセットを取得するクールな方法のようです。
しかし、少なくともGCCでは、もう機能していないようです。私は得る
Cannot initialize a variable of type 'int (t::*) with an rvalue of type "int t:: *'
ここで起こっていることの背後に歴史を持っている人はいますか? このようなことはまだ可能ですか?
ウェブの問題 -- 古い本は決して死なない...