我有一个函数来计算在邻居点集上定义的不同变量的梯度。算法总是相同的,但是根据计算的内容,访问邻居的不同成员数据,例如,当计算速度梯度时,使用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);
它应该可能有效(希望编译器在个别实例化中使用常量优化条件),但可读性和可维护性相当低。
一些更好的主意?
答案 0 :(得分:5)
如果您的所有字段都具有相同的类型,则可以轻松使用指向成员的指针:
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);
}
我认为这可能是最有效的方法。它也非常生动。
答案 1 :(得分:2)
指向会员的指针就是您所需要的。类型写为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);
}
答案 2 :(得分:1)
我是Boost.Fusion的忠实粉丝,更具体地说,Boost.Fusion.Map,它可以让你构建一个类型 - &gt;有价值的地图。
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;
当然,既然我们正在讨论模板,根本没有运行时惩罚:)
答案 3 :(得分:0)
如何继承Node
并使用虚拟访问?甚至可以使用CRTP来避免虚拟呼叫。
答案 4 :(得分:0)
您可以将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
中提供getter和setter方法以便访问它们。]