шаблон для доступа к разным членам объектов, принятых в качестве аргументов

StackOverflow https://stackoverflow.com/questions/7317019

  •  26-10-2019
  •  | 
  •  

Вопрос

У меня есть функция для вычисления градиента различной переменной, определенной на наборе соседних точек. Алгоритм всегда одинаковый, но в зависимости от того, что вычисляется, доступ к различным данным соседей доступна, например, при вычислении градиент скорости, использовать Node::velocity, при вычислении градиент стресса, использовать Node::stress. Анкет Как лучше всего не писать одну и ту же функцию несколько раз?

У меня было несколько возможностей:

  1. Пройти функцию Lambda (C ++ 0x) или Callable объект, возвращающий этот конкретный данные, рассматриваемый

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

    Это должно быть, возможно, эффективно (надеясь, что компилятор будет оптимизировать условные с константами в отдельных экземплярах), но читаемость и обслуживаемость довольно низкие.

Какая -то лучшая идея?

Это было полезно?

Решение

Указатель на участник - это то, что вам нужно. Тип написан как 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 регион затем предоставляет методы Getter и Setter в Node для доступа к ним.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top