2008-08-26 9 views
4

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

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

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

Что вы скажете?

ответ

4

Я думаю, что поеду за ним.

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

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

0

Разве это не то, что find_if для?

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

0

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

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

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

0

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

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

Предикаты "не должны применять какую-либо непостоянную функцию через разыменованные итераторы" (пп. 7 и 8). Я полагаю, это означает, что они не обязаны использовать энергонезависимые методы и что ваш прецедент, таким образом, соответствует стандарту.

Если формулировка «предикаты должны применять функции const ...» или что-то в этом роде, я бы сделал вывод, что функции «const volatile» были не в порядке. Но это не так.

0

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. 
+0

`for_each()` не удовлетворяет этому требованию вопроса: «Я хотел бы связаться с ними по одному, пока вы не ответили успешно». Это продолжается и связывается со всеми контактными серверами в контейнере. Затем в конце пользователь получает информацию только о последней контактной найденной, а не о первой, или, по крайней мере, о всех из них ... не то, что они, похоже, хотят получить информацию о каких-либо. – 2017-05-11 02:16:58

0

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

4

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

+1

Точно, что я думал, алгоритмы STL предназначены не только для контейнеров целых чисел. – 2010-05-09 21:13:40

1

По моему мнению, использование 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; 
    // } 
} 
} 

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

 Смежные вопросы

  • Нет связанных вопросов^_^