C ++でジェネリック型にメンバー変数ポインターをキャストする方法
-
05-07-2019 - |
質問
アプリケーションに次のようなコードがあります:
class A
{
public: int b;
}
class C
{
public: int d;
}
void DoThings (void *arg1, MYSTERYTYPE arg2);
A obj_a;
C obj_c;
DoThings(&obj_a, &A::b);
DoThings(&obj_c, &C::d);
問題は-MYSTERYTYPEはどうあるべきか?値<!> amp; A :: bがprintfを介して出力された場合は正常に印刷されますが、void *もintも機能しません。
説明:はい、<!> amp; A :: bはC ++で定義されています。はい、クラスメンバーへのオフセットを取得しようとしています。はい、トリッキーです。
編集:ああ、offsetof()を使用できます。とにかくありがとう。
解決
2つの無関係なクラスへのデータメンバーポインターがあります。さて、両方のポインタを保持できる一般的な型は見つかりません。関数パラメーターが派生のメンバーへのデータメンバーポインターである場合にのみ機能します。ベースに含まれている場合、メンバーも含まれることが保証されているためです。
struct a { int c; }; struct b : a { }; int main() { int b::*d = &a::c; }
更新:上記がa::*
からb::*
に暗黙的に変換する理由を書く必要があると思います。結局のところ、通常はb*
からa*
まであります!考慮:
struct a { };
struct b : a { int c; };
struct e : a { };
int main() { int a::*d = &b::c; e e_; (e_.*d) = 10; /* oops! */ }
上記が有効な場合、あなたは本当に大失敗するでしょう。 e
からb
への変換は暗黙的ではないため、上記は無効です。ご覧のとおり、b :: cにポインターを割り当て、それをまったく含まないクラスを使用して逆参照することができます。 (a
)。コンパイラーは次の順序を強制します。
int main() { int b::*d = &b::c; e e_; (e_.*d) = 10; /* bug! */ }
<=>は<=>(メンバーポインターポインターが属するクラス)から派生したものではないため、今すぐコンパイルできません。良い!ただし、以下は非常に有効であり、もちろんコンパイルされます(クラス<=>および<=>が変更されました):
struct a { int c; };
struct b : a { };
struct e : a { };
int main() { int e::*d = &a::c; e e_; (e_.*d) = 10; /* works! */ }
ケースに合わせて機能させるには、関数をテンプレートにする必要があります:
template<typename Class>
void DoThings (int Class::*arg) { /* do something with arg... */ }
現在、コンパイラは、指定されたメンバーポインターが属する適切なクラスを自動推論します。インスタンスを実際に使用するには、メンバーポインターと一緒にインスタンスを渡す必要があります。
template<typename Class>
void DoThings (Class & t, int Class::*arg) {
/* do something with arg... */
(t.*arg) = 10;
}
DoThingsの作成時に既に知っているメンバーを設定する場合は、次のコマンドで十分です。
template<typename Class>
void DoThings (Class & t) {
t.c = 10;
}
他のヒント
単にA
またはC
オブジェクト内に存在する整数のアドレスを使用して関数を呼び出そうとしていますか?その場合、Jeff McGlynnの答えは進むべき道です。
それ以外の場合、C ++のメンバーへの奇妙なポインタ機能を必要とするトリッキーな操作を本当に実行しようとしている場合(そして、ほぼ確実にそうではありません):
クラス<=>と<=>は無関係であるため、両方を処理するテンプレート関数が必要になります。
template <typename T>
void DoThings(int T::*x);
<=>が実際に<=>から派生した場合、次のように動作します:
void DoThings(int A::*x);
<!> amp; A :: bおよび<!> amp; C :: dは無意味であり、関連付けられたアドレスはありません。メンバーのオフセットを取得しようとしていますか?
次のようなものは必要ないのですか?
DoSomething(&obj_a,&obj_a.b);
テンプレートをj_random_hackerが示唆するように使用し、コンパイラが関数を呼び出すポイントで各クラスのタイプを知っている場合、質問に対するリテラルの答えは<!> quot; template <typename CLASS> void DoThings (CLASS * object, int CLASS::*MEMBER)
<!> quot;です。
例にどのように適合するかを以下に示します。
#include <iostream>
class A {
public:
int b;
};
class C {
public:
int d;
};
template <typename CLASS>
void DoThings (CLASS * object, int CLASS::*MEMBER)
{
std::cout << object->*MEMBER << std::endl;
}
A obj_a = { 2 };
C obj_c = { 4 };
int main (int argc, const char * argv[])
{
DoThings(&obj_a, &A::b);
DoThings(&obj_c, &C::d);
return 0;
}