将手写循环转换为标准库调用
Transform hand written loop to std library call
我最近看了Sean Parent关于2013年C++调味料的演讲。如果我理解正确,他说你可以从代码中删除几乎所有(所有?(手写循环。我的问题是如何实现这一目标? 让我们考虑以下代码:
class ProgressDialog
{
//interesting part of that class
void SetPosition(int position);
bool IsCancelRequested();
void SetHeader(const std::string& status);
}
void foo( const std::vector<std::string>& v)
{
ProgressDialog dlg;
long position = 0;
for( const auto& s : v)
{
++position;
dlg.SetPosition(position);
dlg.SetHeader("Processing"+ s);
DoSomethingThatTakesSomeTime(s);
if(dlg.IsCancelRequested()) break;
}
}
有没有办法重构手写循环?
我不确定这是否增加了任何清晰度,但它试图重构循环和早期中断的概念。
#include <string>
#include <vector>
#include <ciso646>
struct ProgressDialog
{
//interesting part of that class
void SetPosition(int position);
bool IsCancelRequested();
void SetHeader(const std::string& status);
};
void DoSomethingThatTakesSomeTime(std::string const&);
// iterate over a container, calling func with the current value.
// if func returns false, cease iterating immediately.
// return true if terminated early
// note: func must return false if it wishes early termination,
// otherwise true
template<class Cont, class F>
auto for_each_while(Cont&& container, F&& func)
{
for(auto&& s : container)
if (not func(s))
return true;
return false;
}
void foo( const std::vector<std::string>& v)
{
auto update_dialog =
[position = 0, dlg = ProgressDialog()](auto&& s) mutable
{
++position;
dlg.SetPosition(position);
dlg.SetHeader("Processing"+ s);
DoSomethingThatTakesSomeTime(s);
return !dlg.IsCancelRequested();
};
for_each_while(v, update_dialog);
}
这里有一些性病库滥用,它实现了同样的事情。
我强烈建议你不要这样做,因为普通读者不清楚发生了什么!
void foo( const std::vector<std::string>& v)
{
int position = 0;
auto dlg = ProgressDialog();
std::find_if(begin(v), end(v),
[&](auto&& s)
{
++position;
dlg.SetPosition(position);
dlg.SetHeader("Processing"+ s);
DoSomethingThatTakesSomeTime(s);
return dlg.IsCancelRequested();
});
}
一般情况
如果我理解正确,他说你可以从代码中删除几乎所有(所有?(手写循环。
好吧,是也不是 - 显然这取决于你写什么样的循环。我相信,他有点夸张地强调了这样一个事实,即许多循环并不是真正必要的,并且可能更好地重构为标准库"算法"的应用。
此外,基本上任何循环都可以替换为std::for_each
,但这并不算数,因为它只隐藏了显式循环进度控制,并且仍然"只是一个循环"。
您的具体示例
在您的情况下,循环迭代确实有效,同时记录/报告正在完成的工作的每个迭代,并愿意接受中止请求。您可以考虑编写具有该功能附加方面的std::for_each
变体,然后将其作为特例用于实际工作,例如
namespace with_progress_dialog {
template< class InputIt, class UnaryFunction >
void for_each(
InputIt first,
InputIt last,
UnaryFunction function,
std::string_view message_prefix = "Processing item " )
{
ProgressDialog progress_dialog;
for (position_t position = 0; first != last; ++first, ++position)
{
progress_dialog.SetPosition(position);
progress_dialog.SetHeader(message_prefix + position);
function(*first);
if ( progress_dialog.IsCancelRequested() ) { break; }
}
}
}
然后致电
// ... etc. etc. ...
with_progress_dialog::for_each(
std::begin(v), std::end(v),
&DoSomethingThatTakesSomeTime);
现在,这肯定有点过度概括。但我假设您还有其他情况,在这种情况下,您打开一个逐渐更新的进度对话框。因此,也许可以相应地调整您的概括。或者 - 也许您可以在执行繁重工作时保持某种窗口级状态,并有另一个线程跟踪并在必要时打开对话框窗口,或在状态栏上显示指示。还有另一种选择可能是让该对话在 co-rountine 中运行(但这非常投机,不确定这是一个好主意(。
注意:继续打印您正在处理的字符串不是一个好主意,而且可能不安全 - 这可能源于您无法控制的输入。您需要确保它们不会太长,并考虑对其进行消毒。在我的代码中,我只打印索引("位置"(。
- 将手写循环转换为标准库调用
- C++:带有大括号初始化列表的函数调用表达式 - 标准是否规定在单个元素列表的微不足道的情况下忽略大括号?
- 该标准是否说明了例外和不同调用约定的共存
- 在 c++ 中创建 dll 并在 delphi 中调用的标准方法
- C++ 线程时出错,标准::调用:
- 在使用标准向量函数时引发'std::bad_alloc'实例后调用的终止
- 在 MASM 中调用标准库函数
- 从 ASM 调用C++代码中标准库的链接
- 如何查看我从 c++ 标准库中调用了哪些函数?
- 包装标准::线程调用函数
- C 标准的哪一部分涵盖通过空指针调用方法
- C++17标准对在nullptr上调用delete有何规定
- 重定向标准输出时调用 dup2 失败
- 是否可以保证C++标准库容器调用可替换的新功能
- 修复此标准::异步调用
- 生成标准输出仅在 4098 字节输出后调用
- 通过引用查找调用范围的标准引用是什么
- 是否存在标准调用约定
- 通过对dll的标准调用操作字符串的问题
- c++-builder:转换为标准调用类型