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

发布于 2024-07-04 13:19:17 字数 343 浏览 6 评论 0原文

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

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

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

你怎么说?

Let's say I have a list of server names stored in a vector, and I would like to contact them one at a time until one has successfully responded. I was thinking about using STL's find_if algorithm in the following way:

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

Where ContactServer is a predicate function object.
On one hand, there's a problem since the predicate will not always return the same result for the same server name (because of server downtime, network problems, etc...). However, the same result will be returned regardless of which copy of the predicate is used (i.e. the predicate has no real state), so the original problem with state-keeping predicates is not relevant in this case.

What do you say?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

假装爱人 2024-07-11 13:19:17

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.

std::for_each might be a better candidate for this.

1) After being copied in the same function object is used on each element and after all elements are processed a copy of the potentially updated function object is returned to the user.

2) It would improve the readability of the call in my opinion as well.

The function object and the for_each call would look something like:


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.
说好的呢 2024-07-11 13:19:17

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

find_if appears to be the right choice here. The predicate isn't stateful in this situation.

画▽骨i 2024-07-11 13:19:17

在我看来,std::find_if 的这种用法有点误导。 当我阅读这段代码时,我不希望发生任何副作用,我只是希望找到一个服务器名称。 find_if 的结果被丢弃这一事实也让我怀疑代码是否真的正确。 也许谓词的另一个名称会让意图更清晰,但我认为问题更根本。

对于大多数人来说,find_if 是一种查询算法,而不是修改算法。 即使您实际上没有修改迭代的值,您也正在修改应用程序的全局状态(在这种情况下,您甚至可能修改远程服务器的状态)。

在这种情况下,我可能会坚持使用手动循环,特别是现在 C++11 引入了基于范围的 for 循环:

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

另一个解决方案是将其封装在一个函数中,其名称更能清楚地传达意图,例如 < code>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;
    // }
}
}

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

In my opinion, this use of std::find_if is a bit misleading. When I read this piece of code, I don't expect any side effects to occur, I just expect a server name to be found. The fact that the result of find_if is discarded would also make me wonder if the code is really correct. Perhaps another name for the predicate would make the intent clearer, but I think the problem is more fundamental.

For most people, find_if is a querying algorithm, not a modifying algorithm. Even though you are not actually modifying the values iterated upon, you are modifying the global state of your application (in this case, you are even possibly modifying the state of distant servers).

In such a case, I would probably stick with a manual loop, especially now that C++11 introduced range-based for loops:

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

Another solution would be to encapsulate this in a function with a name conveying more clearly the intent, such as apply_until or something like that:

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;
    // }
}
}

But perhaps I'm being over-puristic :)!

む无字情书 2024-07-11 13:19:17

即将发布的 C++ 标准版本,对于谓词应始终为相同输入返回相同值这一事实,我找不到任何明确的限制。 我研究了第 25 节(第 7 至 10 段)。

返回可能从一个调用到另一个调用而改变的值的方法(如您的情况)应该是易失性的(从 7.1.6.1/11 开始:“易失性是对实现的一个提示,以避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测到的方式进行更改”)。

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

如果措辞是“谓词应该应用 const 函数...”或类似的内容,那么我会得出结论,“const 易失性”函数是不行的。 但这种情况并非如此。

In the upcoming version of the C++ standard, I could not find any explicit restriction with respect to the fact that a predicate should always return the same value for the same input. I looked into section 25 (paragraphs 7 to 10).

Methods returning values that may change from one call to another, as in your case, should be volatile (from 7.1.6.1/11: "volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation").

Predicates "shall not apply any non-constant function through the dereferenced iterators" (paragraphs 7 and 8). I take this to mean that they are not required to use non-volatile methods, and that your use-case is thus ok with respect to the standard.

If the wording was "predicates should apply const functions..." or something like that, then I would have concluded that 'const volatile' functions were not ok. But this is not the case.

卷耳 2024-07-11 13:19:17

我想我会这么做。

我唯一担心的是它的可读性(以及可维护性)。 对我来说,它的内容类似于“查找我可以联系的第一个服务器”,这非常有道理。

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

I think I would go for it.

The only thing I would worry about is the readability (and therefore maintainability) of it. To me, it reads something like "Find the first server I can contact", which makes perfect sense.

You might want to rename ContactServer to indicate that it is a predicate; CanContactServer? (But then people would complain about hidden side effects. Hmm...)

山人契 2024-07-11 13:19:17

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

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

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

However, the same result will be returned regardless of which copy of the predicate is used (i.e. the predicate has no real state), so the original problem with state-keeping predicates is not relevant in this case.

So where's the problem? Function objects don't necessarily have to be stateful. It's actually best practice to use function objects instead of function pointers in such situations because compilers are better at inlining them. In your case, the instantiation and call of the function object may have no overhead at all since find_if is a function template and the compiler will generate an own version for your functor.

On the other hand, using a function pointer would incur an indirection.

无名指的心愿 2024-07-11 13:19:17

这不是 find_if 的用途吗?

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

Isn't that what find_if is for?

Note though, that it will find all the servers, if you iterate over the iterator - but you aren't going to do that (according to OP).

忱杏 2024-07-11 13:19:17

这正是 STL 算法的用途。 这根本不是滥用行为。 此外,它的可读性很强。 重定向至 null 任何告诉您其他情况的人。

This is exactly what the STL algorithms are for. This is not an abuse at all. Furthermore, it is very readable. Redirect to null anyone who tells you otherwise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文