【问题标题】:For loops vs standard library algorithms with a relatively old compiler使用相对较旧的编译器的 for 循环与标准库算法
【发布时间】:2015-06-04 04:48:54
【问题描述】:

我知道当代码中没有任何令人困惑的 for 循环时,代码会更好。尽可能重用标准库算法总是好的。但是,我发现迭代器和算法的语法看起来真的很混乱。

我想举一个我当前项目中的真实例子:我想将vector<vector<QString>> in的内容复制到vector<QVariant> out。我看不出两者之间的区别:

for (int i = 0; i < in[0].size(); i++ ) 
{ 
    if(in[0][i].isNull() || in[0][i].isEmpty() ) 
        out[i] = "NONE";
    else
        out[i] = in[0][i];
}

还有:

std::transform(in[0].begin(), in[0].end(), out.begin(), [](const QString& a)->QVariant{
    if(a.isNull() || a.isEmpty() ) 
        return "NONE";
    else
        return a;
}); 

由于我们有 Visual Studio 2012,我什至必须输入我的 lambda 的返回值。使用范围后:

in[0].map!( a => a.isNull() || a.isEmpty() ? "NONE" : a ).copy(out);

在 D 语言中,我无法忍受上面的 std::transform 代码。而且我什至不确定它是否比基本的for 循环更好。我的问题是:上面使用std::transform 的代码是否比for 循环更好?

【问题讨论】:

  • VS2012 已经实现了 lambda 表达式中返回类型的类型推导。您不必指定它。但杰里·科芬的答案更好。

标签: c++ algorithm c++11


【解决方案1】:

至少在我看来,这里的主要问题是transform 是完全不适合这项工作的工具。

你想要做的正是 std::replace_copy_if 所做的,所以(不足为奇)它做得更整洁。

我手头的机器上没有安装 Qt,所以我冒昧地将您的 QVariantQString 代码替换为 std::vector&lt;std::string&gt;,但我相信相同的基本思想应该适用于Qt 类型也是如此。

#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <string>

int main() {   
    std::vector<std::string> input { "one", "two", "", "three" };
    std::vector<std::string> output;

    // copy input to output, replacing the appropriate strings:
    std::replace_copy_if(input.begin(), input.end(),
                         std::back_inserter(output),
                         [](std::string const &s) { return s.empty(); }, 
                         "NONE");

    // and display output to show the results:
    std::copy(output.begin(), output.end(),
              std::ostream_iterator<std::string>(std::cout, "\n"));
}

目前,这只是将空字符串替换为 NONE,但添加空值检查应该非常简单(当然,对于 isNull 有意义的类型)。

通过上面的数据,我得到了你可能期望的结果:

one
two
NONE
three

然而,我可能应该补充一点,即使这显然也很冗长。当我们至少将范围添加到标准库时会很好,因此(例如)input.begin(), input.end() 可以只替换为input。结果可能仍不会像您提供的 D 代码那样简洁,但至少它在一定程度上减少了冗长(这同样适用于大多数其他算法)。

如果你关心这个,有几个range 库你可能想看看——Boost Range 一个,和(在我看来更有趣)Eric Neibler's range library

【讨论】:

  • 非常感谢您提供的链接。
【解决方案2】:

您的代码可以通过使用? : 来改进(创建一个您可以使用的static QVariant QVNone; 可能是明智的)。

std::transform(in[0].begin(), in[0].end(), out.begin(),
               [](const QString& a)   // for C++14: (auto& a)
                  { return a.isNull() || a.isEmpty() ? QVariant("NONE") : a; }
              );

注意:this page 记录 QVariant(const QString&amp;),因此编译器应该能够为 ? : 值计算出一个通用类型。

当只有一个 return 语句时,C++11 会自动确定 lambda 返回类型 - 请参阅语法 (3) here。 C++14 已经引入了接受参数 ala (auto&amp; a) 的能力。容器元素上的范围将有助于进一步简化此类循环;我认为它们是针对 C++17 提出的;相关论文可用here

还有一些适用于 C++ 的功能(非标准)库,它们可能会为您提供一种更像您为 D 记录的符号的符号。库推荐在这里是题外话,但 Google 应该毫不费力地找到一些候选者。

【讨论】:

  • 我正在处理索引以 1 开头的非常旧的代码库。为了简化我写了 int i = 0 并将 in[0][i-1] 更改为 in[0][i] 之前张贴。我忘记了我现在也编辑过的
  • @KadirErdemDemir:我主要提到了索引错误,以表明无论是在原始写入期间还是在“维护”更改期间,这样的错误是多么容易犯:声明性操作的一个好处(例如标准算法),是因为该类错误的空间较小。我对范围提案没有太大兴趣,但你让我读到了,我自己现在更期待它。干杯。
猜你喜欢
  • 2013-06-11
  • 2017-10-24
  • 2012-05-01
  • 2011-01-31
  • 1970-01-01
  • 1970-01-01
  • 2016-04-01
  • 1970-01-01
  • 2015-03-10
相关资源
最近更新 更多