Вызов функции-члена с одним параметром (привязанным) для каждого объекта в контейнере - PullRequest
0 голосов
/ 18 марта 2012

у меня есть

using namespace std;
typedef vector<Coil*> CoilVec;
CoilVec Coils;

с Coil, являющимся базовым классом для CilCoil и RectCoil, цилиндрической катушкой и прямоугольной катушкой, соответственно. Теперь я хочу вызвать функцию-член calcField для каждого Coil, указанного в Coils. Эта функция-член является чисто виртуальной в базовом классе, но была реализована в производных классах, и ее объявление выглядит следующим образом:

virtual TVector3 calcField(const TVector3&);

, где TVector3 - трехмерный векторный класс из библиотеки RooT. Идея теперь состоит в том, чтобы вычислить поле каждого Coil в Coils и сложить их вместе. Поскольку аргумент calcField (а именно, вектор для позиции, в которой нужно вычислить поле) будет одинаковым для каждого вызова, я хотел бы использовать алгоритм STL из заголовка <algorithm> или <numeric>, чтобы сделать что-то как это (воображаемый):

using namespace std;
typedef vector<Coil*>::const_iterator CoilIt;
const TVector3& P(1.,1.,1.); // Let's say we want to know the total field in (1,1,1)
TVector3 B; // Default initialization: (0,0,0)
CoilIt begin = Coils.begin();
CoilIt end = Coils.end();
B = accumulate(begin, end, B, bind2nd(mem_fun(&Coil::calcField), P));

Очевидно, поскольку я здесь, чтобы задать вопрос, похоже, это не работает. Поэтому мой вопрос довольно прост: почему это не работает и / или как бы вы поступили правильно (в рамках STL)?

Я получаю следующие сообщения об ошибках, пытаясь скомпилировать вышесказанное (файл, в котором я работаю, называется Interface.cpp, это сторонний код):

In file included from /usr/include/c++/4.5/numeric:62:0,
                from Interface.cpp:7: /usr/include/c++/4.5/bits/stl_numeric.h: In function ‘_Tp std::accumulate(_InputIterator, _InputIterator, _Tp, _BinaryOperation) [with _InputIterator = __gnu_cxx::__normal_iterator<Coil* const*, std::vector<Coil*> >, _Tp = TVector3, _BinaryOperation = std::binder2nd<std::mem_fun1_t<TVector3, Coil, const TVector3&> >]’:
Interface.cpp:289:72: instantiated from here
/usr/include/c++/4.5/bits/stl_numeric.h:150:2: error: no match for call to ‘(std::binder2nd<std::mem_fun1_t<TVector3, Coil, const TVector3&> >) (TVector3&, Coil* const&)’
/usr/include/c++/4.5/backward/binders.h:147:7: note: candidates are: typename _Operation::result_type std::binder2nd<_Operation>::operator()(const typename _Operation::first_argument_type&) const [with _Operation = std::mem_fun1_t<TVector3, Coil, const TVector3&>, typename _Operation::result_type = TVector3, typename _Operation::first_argument_type = Coil*]
/usr/include/c++/4.5/backward/binders.h:153:7: note:                  typename _Operation::result_type std::binder2nd<_Operation>::operator()(typename _Operation::first_argument_type&) const [with _Operation = std::mem_fun1_t<TVector3, Coil, const TVector3&>, typename _Operation::result_type = TVector3, typename _Operation::first_argument_type = Coil*]

Ответы [ 4 ]

0 голосов
/ 18 марта 2012

То, что вы хотите, это вложенная привязка. Это невероятно сложно сделать с Устаревшие связующие. Вот упрощенный пример с boost::bind:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <boost/bind.hpp>

struct A {
  double foo(double d) { return d; }
};

int main()
{
  std::vector<A> vs;
  double d = 0.0;
  d = std::accumulate(begin(vs), end(vs), d, 
                      boost::bind(std::plus<double>(), _1, 
                                  boost::bind(boost::mem_fn(&A::foo), _2, 0.0)));
  return 0;
}

Я считаю, что ваше недоразумение заключается в том, что вы предполагаете накапливать использование operator+ для накопления результата функтора, который вы передаете. Факт, что функтор используется для накопления результата. Это основано на общая концепция fold в функциональном программировании.

Функтор, который вы передаете, должен иметь форму

Ret func(const T1& a, const T2& b)

, где Ret должен быть преобразован в T (тип параметра init), T1 должен быть таким, чтобы T мог быть неявно преобразован в него и T2 должно быть таким, чтобы результат разыменования итератора в последовательность должна быть неявно конвертируемой в нее.

0 голосов
/ 18 марта 2012

Результатом вашего вызова bind2nd является унарная функция, которая принимает Coli*. std::accumulate ожидает двоичную функцию, которая может быть вызвана с аккумулятором и текущим значением, то есть она хочет функцию, которая принимает (TVector3, Coli*).

Похоже, вы пытаетесь преобразовать каждый Coli* в TVector3, а затем накапливаете результаты.

Вы можете сделать это с явным шагом std::transform, чтобы заполнить vector<TVector3>, а затем использовать std::accumulate, или вы можете написать отдельную свободную функцию, которая принимает (TVector3 t1, TVector3 t2, Coli* c), возвращает t2 + c->calcField(t1), затем использует bind1st чтобы получить функцию двоичного аккумулятора.

Если вы хотите сделать это без написания отдельной бесплатной функции, вам понадобится boost::bind или C ++ 11.

0 голосов
/ 18 марта 2012

Вероятно, не стоит потрудиться, чтобы ваш код компилировался с mem_fun, bind2nd и друзьями. Я настоятельно рекомендую написать вместо этого хороший старомодный цикл:

for (CoilIt it = coils.begin(); it != coils.end(); ++it)
{
    B += (**it).calcField(P);
}
0 голосов
/ 18 марта 2012

Функция, переданная в std::accumulate(), должна быть двоичной функцией, при каждом вызове функции будут заданы два параметра (уже накопленное значение и следующий элемент вектора).

Связанная функция, которую вы передаете std::accumulate(), принимает только один параметр, Coil*. Для использования с accumulate необходимо принять два параметра (Vector3D и Coil*).

...