20

C++ でクラス定義が与えられた場合

class A
{
  public:
    //methods definition
    ....

  private:
    int i;
    char *str;
    ....
}

C++ テンプレートのメタプログラミングを使用して、コンパイル時にクラス メンバーのオフセットを計算することは可能ですか? クラスは POD ではなく、仮想メソッド、プリミティブ、およびオブジェクト データ メンバーを持つことができます。

4

4 に答える 4

16

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、定数式にすることができます。

于 2013-11-22T09:20:17.937 に答える
6

ええと...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を持つことを禁じることはありません。virtualconstexpr

それでも、これはかなりの障害です。評価されていないコンテキストでは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は、ベースオブジェクトの実行時の配置のため、継承に関するものです。もしあれば、他の欠陥があれば喜んで提示されます。

于 2012-11-01T17:23:34.367 に答える
3

いいえ、一般的ではありません。

offsetof マクロは POD (plain old data) 構造体用に存在し、C++0x で標準レイアウト構造体 (または他の同様のわずかな拡張) にわずかに拡張される場合があります。したがって、これらの制限されたケースについては、解決策があります。

C++ は、コンパイラ作成者に多くの自由を提供します。一部のクラスがクラスのメンバーへの可変オフセットを持つことを防ぐ句については知りませんが、コンパイラがそれを行う理由もわかりません。;)

ここで、コード標準に準拠しつつもオフセットを維持するための 1 つのアプローチは、offsetof が機能する POD (または一部の C++0x 拡張) サブ構造体にデータを貼り付けてから、そのサブ構造体で作業することです。クラス全体ではなく構造体。または、標準への準拠を放棄することもできます。クラス内の構造体のオフセットはわかりませんが、構造体内のメンバーのオフセットはわかります。

重要な質問は、「なぜこれが必要なのか、そして本当に正当な理由があるのか​​」ということです。

于 2012-11-01T16:42:26.750 に答える
0

元の 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:: *'

ここで起こっていることの背後に歴史を持っている人はいますか? このようなことはまだ可能ですか?

ウェブの問題 -- 古い本は決して死なない...

于 2016-11-23T03:18:28.937 に答える