Vorlage zum Zugriff auf verschiedene Mitglieder von Objekten, die als Argumente übergeben wurden

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

  •  26-10-2019
  •  | 
  •  

Frage

Ich habe eine Funktion, um den Gradienten unterschiedlicher Variable zu berechnen, die auf dem Satz von Nachbarnpunkten definiert sind. Der Algorithmus ist immer gleich, aber je nachdem, was berechnet wird, werden verschiedene Mitgliederdaten der Nachbarn zugegriffen, z. Geschwindigkeitsgradient, verwenden Node::velocity, beim Berechnen Stressgradient, verwenden Node::stress. Was ist der beste Weg, um nicht mehrmals dieselbe Funktion zu schreiben?

Ich hatte mehrere Möglichkeiten im Sinn:

  1. Übergeben Sie die Lambda -Funktion (C ++ 0x) oder ein Callable -Objekt, das diese jeweiligen fraglichen Mitgliedsdaten zurückgibt, die als ähnlich bezeichnet werden

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

    Der Minus ist bei jeder Lektüre zusätzlichen Funktionsaufruf.

  2. Vorlage die Funktion basierend auf Ganzzahl und sagt, was berechnet wird:

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

    Es sollte möglicherweise effizient sein (der Hoffnung Compiler wird die Bedingung mit Konstanten in einzelnen Instantien optimieren), aber die Lesbarkeit und Wartbarkeit ist ziemlich niedrig.

Eine bessere Idee?

War es hilfreich?

Lösung

Zeiger auf das Mitglied ist das, was Sie brauchen. Der Typ ist geschrieben als T S::* T ist der Typ des Datenmitglieds, S ist Ihre Struktur oder Klasse. Hier ist ein kleines Beispiel:

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

Andere Tipps

Wenn alle Ihre Felder die gleichen Typen haben, können Zeiger für Mitglieder einfach verwendet werden:

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

Aktualisieren: Wenn nicht, ist es immer noch einfach, Hinweise für Mitglieder mit Vorlagen zu kombinieren:

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

Ich denke, dies ist wahrscheinlich der effizienteste Weg. Es ist auch ziemlich lebendig.

Ich bin ein großer Fan von Boost.fusion und genauer gesagt die Boost.fusion.Map, Mit der Erstellung eines Typs -> Wertschiffung.

struct Velocity {};
struct Stress {};

typedef boost::fusion::map<
  std::pair<Velocity, double>,
  std::pair<Stress, int>
> Map;

Map map;

Jetzt können Sie mit Typen auf die Karte zugreifen:

boost::fusion::at_key<Velocity>(map)

Gibt einen Verweis auf eine Variable vom Typ zurück boost::fusion::result_of::at_key<Velocity, Map>::type

Mit entsprechender Verpackung bekommen Sie:

extern Velocity const velocity;
extern Stress const stress;

myItem.access(stress) = 3;

Und natürlich, da wir über Vorlagen sprechen, überhaupt keine Laufzeitstrafe :) :)

Was ist mit der Erbung von Node Und virtuellen Zugriff verwenden? Es wäre sogar möglich, CRTP zu verwenden, um den virtuellen Anruf zu vermeiden.

Sie können kombinieren velocity, stress, something in einem einzelnen Array und zugreifen auf sie basierend auf enum Index.

struct Node
{
  int attributes[3]; // contains 'velocity', 'stress', 'something';
  enum { VAL_VELOCITY=0, VAL_STRESS, VAL_SOMETHING };
};

Verwendungszweck:

Node n;
n.attributes[Node::VAL_VELOCITY] = <value>;  // writing 'Node::velocity'
<otherthing> = n.attributes[Node::VAL_SOMETHING]; // reading 'Node::something'

Hinweis: Wenn Sie behalten möchten attributes Innerhalb private Region bietet dann Getter- und Setter -Methoden in Node zum Zugriff auf sie.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top