1

我有一个函数来计算在一组相邻点上定义的不同变量的梯度。算法总是相同的,但是根据计算的内容,访问邻居的不同成员数据,例如,当计算速度梯度时,使用Node::velocity,当计算应力梯度时,使用Node::stress。避免多次编写相同函数的最佳方法是什么?

我想到了几种可能性:

  1. 传递 lambda 函数 (c++0x) 或返回有问题的特定成员数据的可调用对象,称为

    gradVelocity=computeGradient(listOfNeighbors,[](const Node& n){ return n.velocity; });
    

    减号是每次读取时的额外函数调用。

  2. 基于整数的模板函数说明正在计算的内容:

    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);
    

    它应该是高效的(希望编译器会在单个实例化中优化条件,去掉常量),但可读性和可维护性非常低。

有更好的主意吗?

4

5 回答 5

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);
}

我认为这可能是最有效的方法。也很生动。

于 2011-09-06T08:48:39.103 回答
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);
}
于 2011-09-06T08:49:53.757 回答
1

我是 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;

当然,因为我们在谈论模板,所以完全没有运行时惩罚:)

于 2011-09-06T08:51:59.090 回答
0

继承Node和使用虚拟访问呢?甚至可以使用 CRTP 来避免虚拟呼叫。

于 2011-09-06T08:37:01.963 回答
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'

[注意:如果您想保留attributesprivate区域内,请提供 getter 和 setter 方法Node来访问它们。]

于 2011-09-06T08:37:52.887 回答