是的,实际上*。 <algorithm> 中的循环结构似乎比优化下的原始循环执行略好,至少在 gcc 中是这样。
乍一看,使用<algorithm> 和 lambda 是一种很好的方法:
bool onlyAlnum(const std::string& str){
return std::all_of(
str.begin(),
str.end(),
[loc = std::locale{}](char c){return std::isalnum(c, loc);});
}
但是这有它的缺点。
locale:当我去测试它时,isalnum 的locale 版本似乎比函数的<cctype> 化身慢了很多。 cctype 版本不关注语言环境,但测试单个char 是否它们是字母数字无论如何都适用于一小部分 UTF-8 字符:UTF-8 是可变宽度编码,并且测试多字符字符的一部分将导致字母数字测试的假阴性。
上面的 lambda 是一个 c++14 的 lambda,它初始化了一个变量 loc 以在首次创建时保存语言环境。这允许函数依赖于当前的语言环境工作,同时也避免了每次评估谓词时构建一个新对象来表示语言环境的成本,就像 lambda 一样:
[](char c){return std::isalnum(c, std::locale{});}
但是,相比之下,它仍然是一个非常缓慢的测试。如果我们不需要std::isalnum 的<locale> 化身的有限优势,我们可以使用(更快)更快的<cctype> 版本:
[](char c){return std::isalnum(c);}
所以我们采用了第二种方法,它使用 cctype 版本。测试表明这要快得多,与您提供的原始循环相当:
bool onlyAlnumAllOf(const std::string& str){
return std::all_of(
str.begin(),
str.end(),
[](char c){return std::isalnum(c);});
}
all_of 测试条件是否对输入迭代器范围内的每个条目都有效。范围由前两个参数提供,这里是str.begin() 和str.end(),它们自然地定义了字符串的开始和结束。
而demo on coliru 表明onlyAlNum 将为任何仅包含字母或数字字符的字符串返回true,但不包含空格。
最后,您可以测试差异。通过粗略的测试评估“oyn3478qo47nqooina7o8oao7nroOL”1000000 次,结果如下:
我机器上 gcc 5.2.0 的 MinGW-64 端口
g++ main.cpp -Wall -Wextra -Wpedantic --std=c++14 -o3
all_of (with locale information): 652ms for 1000000 iterations
all_of: 63ms for 1000000 iterations
find_if: 63ms for 1000000 iterations
loop: 70ms for 1000000 iterations
range-loop: 69ms for 1000000 iterations
和coliru 使用 gcc 6.1.0:
g++ main.cpp -Wall -Wextra -Wpedantic --std=c++14 -o3
all_of (with locale information): 1404ms for 1000000 iterations
all_of: 101ms for 1000000 iterations
find_if: 110ms for 1000000 iterations
loop: 108ms for 1000000 iterations
range-loop: 119ms for 1000000 iterations
在 coliru 上使用 clang 3.8.0:
clang++ -std=c++14 -O3 -Wall -Wextra -Wpedantic main.cpp
all_of (with locale information): 1127ms for 1000000 iterations
all_of: 85ms for 1000000 iterations
find_if: 72ms for 1000000 iterations
loop: 128ms for 1000000 iterations
range-loop: 88ms for 1000000 iterations
如您所见,它因编译器和版本而异,哪个函数最快。优化不好玩吗?
这是我用来测试每个方法的函数:
using StrEvaluator = bool (*)(const std::string&);
using Milliseconds = std::chrono::milliseconds;
void testStrEvaluator(StrEvaluator eval, std::string str){
auto begin = std::chrono::steady_clock::now();
bool result = true;
for(unsigned int i = 0; i < 1000000; ++i){
str.resize(str.size());
result &= eval(str);
}
auto end = std::chrono::steady_clock::now();
std::cout
<< std::chrono::duration_cast<Milliseconds>(end - begin).count()
<< "ms for 1000000 iterations\n";
}
测试存在缺陷:coliru 不保证执行期间资源的一致性,而且我没有关闭计算机上的其他程序,因此变化可能是侥幸。但是,它们似乎足够一致,可以从中得出一些结论:算法和原始循环的循环结构都可以很好地执行,并且根据速度在它们之间进行选择(除非您发现循环是瓶颈)更加微观-优化比什么都重要。