掌握C++17的“武器“:Boost库带来的新特性
掌握C++17的“武器“:Boost库带来的新特性
C++17引入了许多新特性,其中许多都是从Boost库移植过来的。本文将详细介绍这些特性,包括搜索算法、文件系统库、特殊数学函数、模板增强等,并简要介绍C++20的一些特性。
一、简介
在上一篇文章中,我们介绍了几个特性:std::optional
、std::variant
、std::any
和string_view
。本文将继续讨论更多的元素:std::filesystem
、搜索器等等!还将对C++20进行简单介绍。
众所周知,Boost库提供了大量标准库中没有的方便算法、类型和特性。许多功能被“移植”到核心C++中。例如,在C++11中,获得了std::regex
、线程和智能指针。
对于C++17,从Boost中采用了以下特性:
- 词汇类型:
std::variant
、std::any
、std::optional
。 string_view
。- 搜索算法:Boyer Moore和Boyer Moore Horspool。
std::filesystem
。- 特殊数学函数。
template
的改进。
二、搜索算法
Boost提供了三种模式搜索算法:
- Knuth-Morris-Pratt算法,
- Boyer-Moore算法,
- Boyer-Moore- horspool算法。
通过使用预处理步骤,所有的算法都击败了大字符串的普通模式搜索。它们根据输入模式构建额外的表,这样搜索就更有效了。
最后两个算法被移植到C++17中,它们可以作为std::search
函数的附加搜索对象使用。现在,C++17为std::search
提供了一个新的重载:
template<class ForwardIterator, class Searcher>
ForwardIterator search( ForwardIterator first, ForwardIterator last,
const Searcher& searcher );
Searcher
是一个模板参数(所以也可以自己实现),标准库提供了三种类型:
default_searcher
。boyer_moore_searcher
。boyer_moore_horspool_searcher
。
使用方式:
std::string testString = "Hello Super World";
std::string needle = "Super";
auto it = search(testString.begin(), testString.end(),
boyer_moore_searcher(needle.begin(), needle.end()));
if (it == testString.end())
cout << "The string " << needle << " not found\n";
为每个模式创建一次搜索器对象。如果希望在不同的容器中搜索相同的文本,那么可以节省一些预处理时间。
在一些性能实验中,对于更大的模式和boyer_moore
,可以获得比默认搜索器更好的性能。例如,当扫描包含547412个字符的文本内部并查找200个字母的模式时,获得了比默认搜索器快8倍的性能加速。甚至比优化后的std::string::find
性能提高了3倍。
三、文件系统库:filesystem
这是对C++17和标准库的一个巨大的补充。委员会在boost::filesystem
方面积累了多年的经验,对其进行了改进,提出了一个技术规范,后来合并到标准中。
典型例子:Boost中的目录迭代。
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
fs::path inputPath = GetInputPath();
for (const auto& entry : fs::directory_iterator(inputPath))
std::cout << entry.path() << '\n';
现在,C++17的版本也有了:
#include <filesystem>
namespace fs = std::filesystem;
fs::path inputPath = GetInputPath();
for (const auto& entry : fs::directory_iterator(inputPath)) {
std::cout << entry.path() << '\n';
觉得有什么不同吗?代码几乎与Boost一样!甚至可以稍微扩展一下,添加更多的日志输出:
#include <filesystem>
namespace fs = std::filesystem;
for (const auto& entry : fs::directory_iterator(inputPath)) {
const auto filenameStr = entry.path().filename().string();
if (entry.is_directory()) {
std::cout << "dir: " << filenameStr << '\n';
}
else if (entry.is_regular_file()) {
std::cout << "file: " << filenameStr << '\n';
}
else
std::cout << "?? " << filenameStr << '\n';
}
可以看到,在上面的代码中,可以有效地处理路径对象,在目录(无论是否递归)上运行迭代,并打印关于给定目录条目的各种信息。
filesystem
库由四个主要部分组成:
- 路径对象:表示系统中路径的类型。使用各种方法提取路径部分,组合它,格式之间的转换,甚至从字符串到宽字符串。
directory_entry
:保存有关某个目录内的路径的信息,以及缓存。- 目录迭代器:两个允许扫描目录的类(即只扫描一次或递归扫描一次)。
- 加上许多支持性的非成员功能:
- 获取路径信息。
- 文件操作:复制,移动,创建,符号链接。
- 最后写时间。
- 权限。
- 空间/文件大小
- ...
这个库是巨大的,它对依赖文件访问的应用程序是非常有益的,毕竟有哪个应用程序不需要与文件一起工作呢?
四、特殊数学函数:clamp、gcd等
Boost库提供了许多算法和函数,甚至可以帮助进行高级数学计算。例如,有一个完整的Math Toolkit 2.9.0 - 1.70.0模块,几乎包含了数学库中的所有功能。C++17标准用一些额外的函数扩展了这个库。
如clamp
、gcd
和lcm
:
#include <iostream>
#include <algorithm> // clamp
#include <numeric> // for gcm, lcm
int main() {
std::cout << std::clamp(300, 0, 255) << ', ';
std::cout << std::clamp(-10, 0, 255) << '\n';
std::cout << std::gcd(24, 60) << ', ';
std::cout << std::lcm(15, 50) << '\n';
}
还有一组特殊的数学函数:assoc_laguerre
、beta
、comp_ellint_1/_2/_3
、hermite
、laguerre
、rieman_zeta
等等。
这些特殊数学函数的完整列表可以在官方网址 cppreference 中找到。
五、模板增强:and、or、not
P0013文献建议将元函数and_
、or_
和not_
添加到标准库中,并引用Boost.MPL作为长期实现这些特性的标准库之一。该文献在C++17中被采纳为std::conjunction
、std::disjunction
和std::negation
。
示例:
template<typename... Ts>
std::enable_if_t<std::conjunction_v<std::is_same<int, Ts>...> >
PrintIntegers(Ts ... args) {
(std::cout << ... << args) << '\n';
}
上面的函数PrintIntegers
使用可变数量的参数,但它们都必须是int
类型。
六、C++20概览
在C++20中,获得Ranges和Concepts等,但是你知道Boost中也有一个更早的版本吗?这是一个Boost中的Ranges库链接:Boost Range 2.0 - Boost Range。
现在,虽然C++20中的Concept是语言的一部分,但可以使用Boost Concept Check Library来模拟它们。该库在很大程度上基于宏,但也可以获得泛型编程的一些概要,以及通过实际Concept实现目标。
七、总结
希望这篇博文能给你更多开始使用C++17的动力。最新的C++标准不仅提供了许多语言特性(如if constexpr
、结构化绑定、折叠表达式……),而且还提供了来自标准库的一组广泛的实用程序。现在可以使用多种词汇表类型:variant
、optional
、any
。使用字符串视图,甚至一个重要的组件std::filesystem
。所有这些都不需要引用一些外部库。