функция для перебора членов и вызова функции, переданной в качестве аргумента - PullRequest
0 голосов
/ 10 декабря 2010

У меня есть std::vector<T> некоторого типа, который является частью класса и который мне нужно перебирать во многих различных местах в моем коде, поэтому я подумал, что должен быть умным, создать функцию IterateAttributes и передать это объект boost :: function, который я могу использовать в цикле и передать один элемент, а затем я могу передать любую функцию для работы с элементами.

Это кажется хорошей идеей до тех пор, пока вам не придется ее реализовывать, тогда возникает проблема с тем, что возвращает переданная функция и нужны ли ей другие аргументы. Похоже, мне нужно либо найти способ сделать это более обобщенно, например, с помощью шаблонов, либо мне нужно создавать перегрузки с объектами функций, принимающими разные аргументы.

Я думаю, что первый (более общий) вариант, вероятно, лучше, однако, как бы я поступил об этом?

Ниже приведена пробная версия, которая не работает, однако, если бы я хотел иметь несколько аргументов и все, кроме атрибута (структура), обязательного аргумента. Как мне это сделать?

template <typename T> template <typename arg>
void ElementNode::IterateAttributes(boost::function<T (arg, Attribute)> func_)
{
    std::vector<Attribute>::iterator it = v_attributes.begin();

    for (; it != v_attributes.end(); it++)
    {
        func_(arg, *it);
    }
}

Ответы [ 4 ]

1 голос
/ 10 декабря 2010

Это то, что вы имеете в виду:

template <typename T, typename arg>
void ElementNode::IterateAttributes(boost::function<T (arg, Attribute)> func_, arg a)
{
    std::vector<Attribute>::iterator it = v_attributes.begin();

    for (; it != v_attributes.end(); it++)
    {
        func_(a, *it);
    }
}

, который допускает только один параметр любого типа - если вы хотите, вы можете ввести также версию для большего количества параметров.

О возвращаемом значении - чточто делать с этим, зависит от того, какое значение оно имеет на самом деле - общее (и, вероятно, ненужное) решение будет возвращать std::list<T>, но это создаст больше проблем, чем решит, я полагаю.Если тип возвращаемого значения изменяется (не только по типу, но и по значению), я предлагаю изменить шаблонную функцию, чтобы она брала ссылку / указатель на общий результат и обновляла его соответствующим образом:

template <typename T> template <typename arg>
void ElementNode::IterateAttributes(boost::function<voidT (arg, Attribute, T&)> func_)
{
    std::vector<Attribute>::iterator it = v_attributes.begin();
    T result;

    for (; it != v_attributes.end(); it++)
    {
        func_(arg, *it, result);
    }
    return result;
}

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

Если вам нужно переменное количество параметров, вам нужно будет создать несколько шаблонов вышеуказанной функции - я только что проверил, если это возможно:

template <typename T>
 T boo(T){

}

template <typename T, typename TT>
TT boo(T,TT){

}

void test()
{
    int i;
    i= boo<int>(0);
    i=boo<int,double>(0,0.0);
}

Вы должны помнить, что функции, передаваемые в IterateAttributes, должны точно соответствовать параметрам, переданным функции Iterate.Это также означает, что вы не можете использовать в его прототипе значения по умолчанию - возможно, вам придется определить несколько перегруженных версий, таких как

void func_(Attribute,arg1, arg2,arg3){...}
void func_(Attribute A,arg1 a1,arg2 a2){func_(A,a1, a2,default3);}
void func_(Attribute A,arg1 a1){func_(A,a1, default2,default3);}
void func_(Attribute A){func_(A,default1, default2,default3);}
0 голосов
/ 10 декабря 2010

Вы можете сделать то же самое или больше, используя BOOST_FOREACH или C ++ 0x for each. Это даже потребовало бы меньше кода для написания.

0 голосов
/ 10 декабря 2010

Обобщая комментарии и больше мыслей:

Используйте bind, чтобы связать другие аргументы, затем используйте for_each в результирующем функторе.

Для обработки возвращаемых значений вам нужно подумать о том, что означают возвращаемые значения. Если вам нужно каким-то образом использовать значения (скажем, выполнить уменьшение или использовать их, чтобы повлиять на то, следует ли продолжать выполнение операции и т. Д.), Тогда вы можете использовать другой функтор, чтобы обернуть оригинал, чтобы выполнить то, что вы хотите .

0 голосов
/ 10 декабря 2010

a) Вы хотите перебрать массив и сделать что-то с каждым элементом там: в этом случае вам нужны функции, которые все принимают элемент массива и возвращают void. Простой.

b) Вы хотите частично применить функции с большим количеством аргументов для каждого элемента: напишите пользовательский функтор вокруг вашей функции, в котором будут храниться дополнительные предварительно назначенные аргументы, или используйте boost::bind, чтобы эффективно сделать то же самое.

Пример:

vector<string> myStrings; // assign some values

// define a function with an additional argument
void myFunc(string message, string value)
{
  cout << message << value << endl;
}

// allow partial application, i.e. "currying"
struct local_function
{   
  static string message;

  static void myFunc_Curried(string value)
  {
    myFunc(message, value);
  }    
};

local_function::message = "the array contains: ";

// apply the curried function on all elements in the array
for_each(myStrings.begin(), myStrings.end(), local_function::myFunc_Curried);

Функтор работает статически только в демонстрационных целях. Если message привязан к экземпляру структуры, вам все равно понадобится что-то вроде boost::bind, чтобы связать указатель экземпляра this для фактического вызова карри функции. Однако, если функция, которую я хочу применить, используется только локально, я предпочитаю следовать более читабельному статическому подходу.

То, что вы пытаетесь выполнить, имеет очень хороший смысл, а также встроено непосредственно в функциональные языки (например, F #). Это возможно сделать в C ++, но требует некоторых обходных путей в вышеупомянутом случае b. Обратите внимание, что если вы пишете свой собственный функтор, как в моем примере, то обычно размещайте аргументы, которые вы хотите вычеркнуть, всегда в начале и «заполняйте» аргументы от начала до конца при частичном применении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...