对于循环与使用相对较旧的编译器的标准库算法

时间:2015-06-04 04:48:54

标签: c++ algorithm c++11

我知道当代码中没有任何令人困惑的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循环好吗?

2 个答案:

答案 0 :(得分:8)

至少在我看来,这里的主要问题是transform只是工作的错误工具。

你要做的事情正是std::replace_copy_if所做的事情,所以(没有什么大惊喜)它能更清楚地做到这一点。

我手边的机器上没有安装Qt,所以我冒昧地将QVariantQString代码替换为std::vector<std::string>,但我相信相同的基本思想也应该适用于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一个,(在我看来更有趣){{3} }。

答案 1 :(得分:3)

使用? :可以改进您的代码(创建一个可以使用的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&),因此编译器应该能够计算? :值的公共类型。

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

还有用于C ++的功能(非标准)库可能会为您提供更像您为D文档提供的符号。图书馆建议在这里是偏离主题的,但Google应该不费吹灰之力就找出一些候选人。< / p>