这段代码是否滥用了STL的find_if函数?

5

假设我有一个存储在向量中的服务器名称列表,并且我想要逐个联系它们,直到其中一个成功响应。我考虑使用STL的find_if算法进行以下操作:

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

ContactServer是一个谓词函数对象。
一方面,由于服务器宕机、网络问题等原因,该谓词不会始终针对同一服务器名称返回相同的结果。但是,无论使用谓词的哪个副本,都将返回相同的结果(即谓词没有真正的状态),因此在这种情况下,涉及状态保持谓词的原始问题并不相关。

你怎么看?

8个回答

4

这正是STL算法的用途。这并不是滥用。此外,它非常易读。如果有人告诉你相反的话,请将其重定向到null。


1
正是我所想的,STL算法不仅适用于整数容器。 - Viktor Sehr

4

我觉得我会选择这个。

唯一需要担心的是它的可读性(因此也影响可维护性)。对我来说,它的意思是“找到我可以联系的第一个服务器”,这非常合理。

你可能想要将ContactServer重命名为表示谓词的名称;CanContactServer?(但是人们可能会抱怨隐藏的副作用。嗯...)


2
在我看来,这里使用的 std::find_if 有点误导人。当我阅读这段代码时,我不希望发生任何副作用,我只是期望找到一个服务器名称。而且,find_if 的结果被丢弃也会让我怀疑代码是否正确。也许为谓词取另一个名字可以使意图更清晰,但我认为问题更根本。
对于大多数人来说,find_if 是一种查询算法,而不是修改算法。即使您实际上没有修改迭代的值,您也会修改应用程序的全局状态(在这种情况下,您甚至可能修改远程服务器的状态)。
在这种情况下,我可能会坚持使用手动循环,特别是现在 C++11 引入了基于范围的 for 循环。
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;
    // }
}
}

但也许我太过于纯粹主义了 :)!

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.

for_each()不满足问题的要求:“我想一个接一个地联系他们,直到其中一个成功回复”。 这将继续并联系容器中的所有可联系服务器。 然后在最后,用户只获取找到的最后一个可联系的信息,而不是第一个,或者至少是所有... 不过他们似乎不想要任何信息。 - underscore_d

0

在这里,find_if似乎是正确的选择。在这种情况下,谓词不具有状态。


0

这不是 find_if 的作用吗?

但请注意,如果您在迭代器上进行迭代,它将找到所有服务器 - 但根据 OP 的说法,您不会这样做。


0
然而,无论使用谓词的哪个副本,都将返回相同的结果(即谓词没有真正的状态),因此状态保持谓词的原始问题在这种情况下不相关。
那么问题在哪里呢?函数对象不一定是有状态的。在这种情况下,使用函数对象而不是函数指针是最佳实践,因为编译器更擅长内联它们。在您的情况下,函数对象的实例化和调用可能根本没有开销,因为find_if是一个函数模板,编译器将为您的函数对象生成自己的版本。
另一方面,使用函数指针会产生间接性。

0
C++标准的即将到来的版本中,我没有找到任何明确的限制,关于谓词对于相同的输入应该始终返回相同的值。我查看了第25节(第7至10段)。
像您的情况一样,返回可能会从一次调用到另一次调用发生变化的值的方法应该是易失性的(来自7.1.6.1/11:“易失性是对实现的提示,避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测的手段而改变”)。
谓词“不得通过解引用的迭代器应用任何非常量函数”(第7和8段)。我认为这意味着它们不需要使用非易失性方法,并且您的用例因此符合标准。
如果措辞是“谓词应该应用const函数…”或类似的话,那么我会得出结论,'const volatile'函数不可以。但这并不是这种情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接