引数として渡されたオブジェクトのさまざまなメンバーにアクセスするためのテンプレート
-
26-10-2019 - |
質問
隣接点のセットで定義されたさまざまな変数の勾配を計算する関数があります。アルゴリズムは常に同じですが、計算されたものによっては、近隣の異なるメンバーデータにアクセスされます。たとえば 速度の勾配, 、 使用する Node::velocity
, 、コンピューティング時 ストレスの勾配, 、 使用する Node::stress
. 。同じ機能を何度か書くことを避けるための最良の方法は何ですか?
私はいくつかの可能性を念頭に置いていました:
lambda関数(C ++ 0x)または問題の特定のメンバーデータを返すコール可能なオブジェクトを渡します。
gradVelocity=computeGradient(listOfNeighbors,[](const Node& n){ return n.velocity; });
マイナスは、読み取りごとに追加の関数呼び出しです。
計算されているものを示す整数に基づいて関数をテンプレートします。
enum{VAL_VELOCITY=0,VAL_STRESS,VAL_SOMETHING}; template<int what> computeGradient(const std::list<Node>& neighbors){ /*loop over neighbors*/ value=(what==VAL_VELOCITY?neighbor.velocity:((what==VAL_STRESS)?neighbor.stress:neighbor.something); /* and so on */ } /* called like this */ gradVelocity=computeGradient<VAL_VELOCITY>(neighbors);
おそらく効率的である必要があります(コンパイラが個々のインスタンス化で定数を使用して条件付きを最適化することを願っています)が、読みやすさと保守性はかなり低いです。
いくつかのより良いアイデア?
解決
メンバーへのポインターはあなたが必要とするものです。タイプは次のように書かれています T S::*
Tはデータメンバーのタイプであり、Sは構造体またはクラスです。ここに小さな例があります:
#include <iostream>
struct Foo
{
int a;
double b;
Foo(int a, double b)
: a(a), b(b)
{ }
};
template<typename T, T Foo::* mem>
void print(const Foo& foo)
{
std::cout << foo.*mem << std::endl;
}
int main()
{
Foo f(5, 3.14);
print<int, &Foo::a>(f);
print<double, &Foo::b>(f);
}
他のヒント
すべてのフィールドが同じタイプを持っている場合、メンバーへのポインターを簡単に使用できます。
struct Node
{
double stress;
double velosity;
};
void foo(Node* pNode, double Node::*pValue)
{
cout << pNode->*pValue << endl;
}
int main()
{
Node n1 = { 1, 2 };
foo(&n1, &Node::stress);
foo(&n1, &Node::velosity);
}
アップデート: そうでない場合は、メンバーとテンプレートをメンバーに組み合わせるのは簡単です。
struct Node
{
double stress;
double velosity;
int dimension;
};
template<class T>
void foo(Node* pNode, T Node::*pValue)
{
cout << pNode->*pValue << endl;
}
int main()
{
Node n1 = { 1, 2 };
foo(&n1, &Node::stress);
foo(&n1, &Node::velosity);
foo(&n1, &Node::dimension);
}
これはおそらく最も効率的な可能な方法だと思います。それもかなり鮮明です。
私はBoost.Fusionの大ファンです。より具体的には、 boost.fusion.map, 、タイプ - >値のマップの種類を作成できます。
struct Velocity {};
struct Stress {};
typedef boost::fusion::map<
std::pair<Velocity, double>,
std::pair<Stress, int>
> Map;
Map map;
これで、タイプでマップにアクセスできます。
boost::fusion::at_key<Velocity>(map)
タイプの変数への参照を返します boost::fusion::result_of::at_key<Velocity, Map>::type
適切なラッピングで、あなたは取得します:
extern Velocity const velocity;
extern Stress const stress;
myItem.access(stress) = 3;
そしてもちろん、私たちはテンプレートを話しているので、ランタイムペナルティはまったくありません:)
継承するのはどうですか Node
仮想アクセスを使用していますか? CRTPを使用して仮想呼び出しを避けることさえ可能です。
組み合わせることができます velocity
, stress
, something
単一の配列で、それらに基づいてアクセスします enum
索引。
struct Node
{
int attributes[3]; // contains 'velocity', 'stress', 'something';
enum { VAL_VELOCITY=0, VAL_STRESS, VAL_SOMETHING };
};
使用法:
Node n;
n.attributes[Node::VAL_VELOCITY] = <value>; // writing 'Node::velocity'
<otherthing> = n.attributes[Node::VAL_SOMETHING]; // reading 'Node::something'
注:保持したい場合 attributes
中身 private
その後、地域はゲッターとセッターのメソッドを提供します Node
それらにアクセスするため。