这段代码是滥用STL的find_if吗?

时间:2008-08-26 09:10:28

标签: c++ stl

假设我有一个存储在向量中的服务器名称列表,我想一次联系一个,直到一个成功响应。我正在考虑以下列方式使用STL的find_if算法:

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

其中ContactServer是谓词函数对象 一方面,由于谓词不会总是为相同的服务器名返回相同的结果(因为服务器停机,网络问题等等),所以存在问题。但是,无论使用哪个谓词副本(谓词没有实际状态),都将返回相同的结果,因此状态保持谓词的原始问题在这种情况下不相关。

你说什么?

8 个答案:

答案 0 :(得分:4)

我想我会选择它。

我唯一担心的是它的可读性(以及可维护性)。对我来说,它读起来像“找到我能联系的第一台服务器”,这很有道理。

您可能希望重命名ContactServer以表明它是谓词; CanContactServer? (但后来人们会抱怨隐藏的副作用。嗯......)

答案 1 :(得分:4)

这正是STL算法的用途。这根本不是滥用。此外,它非常易读。重定向到任何告诉你的人都无效。

答案 2 :(得分:1)

在我看来,使用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;
    // }
}
}

但也许我过于纯粹了!)!

答案 3 :(得分:0)

这不是find_if的用途吗?

但请注意,如果迭代迭代器,它会找到所有服务器 - 但是你不打算这样做(根据OP)。

答案 4 :(得分:0)

  

但是,无论使用哪个谓词副本(即谓词没有实际状态),都会返回相同的结果,因此状态保持谓词的原始问题在这种情况下不相关。

那么问题出在哪里?函数对象不一定必须是有状态的。在这种情况下使用函数对象而不是函数指针实际上是最佳实践,因为编译器更擅长内联它们。在您的情况下,函数对象的实例化和调用可能没有任何开销,因为find_if是一个函数模板,编译器将为您的仿函数生成一个自己的版本。

另一方面,使用函数指针会产生间接。

答案 5 :(得分:0)

upcoming version of the C++ standard中,我找不到任何关于谓词应该总是为同一输入返回相同值的事实的明确限制。我查看了第25条(第7至10段)。

返回可能从一个调用更改为另一个调用的值的方法(如在您的情况下)应该是易失性的(从7.1.6.1/11开始:“volatile是对实现的暗示,以避免涉及对象的积极优化,因为值对象可能会被实现“)检测不到。

谓词“不应通过解除引用的迭代器应用任何非常数函数”(第7和8段)。我认为这意味着他们不需要使用非易失性方法,因此您的用例在标准方面是可以的。

如果措辞是“谓词应该应用const函数......”或类似的东西,那么我会得出结论'const volatile'函数不正常。但这种情况并非如此。

答案 6 :(得分: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.

答案 7 :(得分:0)

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