Сведения о вопросе

Solllo

06:58, 27th August, 2020

Теги

c++   stl    

Является ли этот код злоупотреблением find_if STL?

Просмотров: 409   Ответов: 8

Допустим, у меня есть список имен серверов, хранящихся в векторе, и я хотел бы связаться с ними по одному, пока один не ответит успешно. Я думал об использовании алгоритма find_if STL следующим образом:

find_if(serverNames.begin(), serverNames.end(), ContactServer());

Где ContactServer-объект функции предиката.
С одной стороны, существует проблема, поскольку предикат не всегда возвращает один и тот же результат для одного и того же имени сервера (из-за простоя сервера, сетевых проблем и т. д...). Однако один и тот же результат будет возвращен независимо от того, какая копия предиката используется ( т. е. предикат не имеет реального состояния), поэтому исходная проблема с предикатами сохранения состояния в этом случае не актуальна.

Что скажешь?



  Сведения об ответе

DAAA

12:19, 25th August, 2020

Я думаю, что пошел бы на это.

Единственное, о чем я бы беспокоился, - это читабельность (и, следовательно, ремонтопригодность) его. Для меня это звучит примерно так:" найдите первый сервер, с которым я могу связаться", что вполне логично.

Возможно, вы захотите переименовать ContactServer , чтобы указать, что это предикат; CanContactServer ? (Но тогда люди будут жаловаться на скрытые побочные эффекты. Хм...)


  Сведения об ответе

SILA

06:53, 21st August, 2020

Именно для этого и предназначены алгоритмы STL. Это вовсе не оскорбление. Кроме того, он очень удобочитаем. Перенаправьте на null любого, кто скажет вам обратное.


  Сведения об ответе

pumpa

21:05, 12th August, 2020

На мой взгляд, это использование std::find_if немного вводит в заблуждение. Когда я читаю этот фрагмент кода, я не ожидаю никаких побочных эффектов, я просто ожидаю, что будет найдено имя сервера. Тот факт, что результат find_if отбрасывается, также заставит меня задаться вопросом, действительно ли код правильный. Возможно, другое название для предиката сделало бы намерение более ясным, но я думаю, что проблема более фундаментальна.

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

В таком случае я бы, вероятно, придерживался ручного цикла, особенно теперь, когда C++11 ввел диапазон на основе циклов:

for (std::string const & name : serverNames)
{
    if (ContactServer(name)) break;
}

Другим решением было бы инкапсулировать это в функцию с именем, более четко передающим намерение, например apply_until или что-то в этом роде:

template <typename InputIterator, typename Function>
void apply_until(InputIterator first, InputIterator last, Function f)
{
    std::find_if(first, last, f);
    // or
    // while (first != last)
    // {
    //     if (f(*first)) break;
    //
    //     ++first;
    // }
}
}

Но, возможно, я черезмерно-пуристический :)!


  Сведения об ответе

LAST

21:17, 27th August, 2020

Разве не для этого существует find_if ?

Обратите внимание, однако, что он найдет все серверы, если вы будете перебирать итератор - но вы не собираетесь этого делать (согласно OP).


  Сведения об ответе

ASER

17:55, 4th August, 2020

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

Так в чем же проблема? Объекты функций не обязательно должны иметь состояние. На самом деле лучше всего использовать объекты функций вместо указателей функций в таких ситуациях, потому что компиляторы лучше встраивают их. В вашем случае создание экземпляра и вызов объекта function могут вообще не иметь никаких накладных расходов, так как find_if является шаблоном функции, и компилятор создаст собственную версию для вашего функтора.

С другой стороны, использование указателя функции повлечет за собой опосредование.


  Сведения об ответе

PROGA

19:19, 11th August, 2020

В следующей версии стандарта C++ я не смог найти никаких явных ограничений в отношении того, что предикат должен всегда возвращать одно и то же значение для одного и того же ввода. Я заглянул в раздел 25 (пункты 7-10).

Методы, возвращающие значения, которые могут изменяться от одного вызова к другому, как и в вашем случае, должны быть летучими (от 7.1.6.1/11: "volatile-это подсказка к реализации, чтобы избежать агрессивной оптимизации с участием объекта, поскольку значение объекта может быть изменено средствами, не обнаруживаемыми реализацией").

Предикаты "shall not apply any non-constant function through the dereferenced iterators" (пункты 7 и 8). Я полагаю, что это означает, что они не обязаны использовать энергонезависимые методы, и что ваш вариант использования, таким образом, соответствует стандарту.

Если бы формулировка была "predicates should apply const functions..." или что-то в этом роде, то я бы сделал вывод, что функции 'const volatile' не были в порядке. Но это не тот случай.


  Сведения об ответе

ITSME

18:30, 9th August, 2020

std::for_each может быть лучшим кандидатом для этого.

1) после копирования в один и тот же объект функции используется на каждом элементе и после обработки всех элементов копия потенциально обновленного объекта функции возвращается пользователю.

2) это также улучшило бы читабельность вызова, на мой взгляд.

Объект функции и вызов for_each будут выглядеть примерно так:


struct AttemptServerContact {
  bool        server_contacted;
  std::string active_server;    // name of server contacted

  AttemptServerContact() : server_contacted(false) {}

  void operator()(Server& s) {
    if (!server_contacted) {
      //attempt to contact s
      //if successful, set server_contacted and active_server
    }
  }
};

AttemptServerContact func;
func = std::for_each(serverNames.begin(), serverNames.end(), func);
//func.server_contacted and func.active_server contain server information.


  Сведения об ответе

dumai

08:39, 10th August, 2020

find_if кажется здесь правильным выбором. Предикат в этой ситуации не является статусным.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться