我们已经零零碎碎提到过几次 Boost 了。作为 C++ 世界里标准库之外最知名的开放源码程序库,我们值得专门用一讲来讨论一下 Boost。
Boost 概览
Boost 的网站把 Boost 描述成为经过同行评审的、可移植的 C++ 源码库(peer-reviewed portable C++ source libraries)[1]。换句话说,它跟很多个人开源库不一样的地方在于,它的代码是经过评审的。事实上,Boost 项目的背后有很多 C++ 专家,比如发起人之一的 Dave Abarahams 是 C++ 标准委员会的成员,也是《C++ 模板元编程》一书 [2] 的作者。这也就使得 Boost 有了很不一样的特殊地位:它既是 C++ 标准库的灵感来源之一,也是 C++ 标准库的试验田。下面这些 C++ 标准库就源自 Boost:
智能指针
thread
regex
random
array
bind
tuple
optional
variant
any
string_view
filesystem
等等
当然,将来还会有新的库从 Boost 进入 C++ 标准,如网络库的标准化就是基于 Boost.Asio 进行的。因此,即使相关的功能没有被标准化,我们也可能可以从 Boost 里看到某个功能可能会被标准化的样子——当然,最终标准化之后的样子还是经常有所变化的。
我们也可以在我们的编译器落后于标准、不能提供标准库的某个功能时使用 Boost 里的替代品。比如,我之前提到过老版本的 macOS 上苹果的编译器不支持 optional 和 variant。除了我描述的不正规做法,改用 Boost 也是方法之一。比如,对于 variant,所需的改动只是:
把包含 <variant> 改成包含 <boost/variant.hpp>
把代码中的 std::variant 改成 boost::variant
这样,就基本大功告成了。
作为一个准标准的库,很多环境里缺省会提供 Boost。这种情况下,在程序里使用 Boost 不会额外增加编译或运行时的依赖,减少了可能的麻烦。如果我需要某个功能,在标准库里没有,在 Boost 里有,我会很乐意直接使用 Boost 里的方案,而非另外去查找。如果我要使用非 Boost 的第三方库的话,那一般要么是 Boost 里没有,要么就是那个库比 Boost 里的要好用很多了。
鉴于 Boost 是一个库集合,当前版本(1.72)有 160 个独立库,即使写本书也不可能完整地讨论所有的库。这一讲里,我们也就管中窥豹式地浏览几个 Boost 库。具体你需要什么,还是得你自己回头去细细品味。
Boost 的安装
在主要的开发平台上,现在你都可以直接安装 Boost,而不需要自己从源代码编译了:
在 Windows 下使用 MSVC,我们可以使用 NuGet 安装(按需逐个安装)
在 Linux 下,我们可以使用系统的包管理器(如 apt 和 yum)安装(按需逐个安装,或一次性安装所有的开发需要的包)
在 macOS 下,我们可以使用 Homebrew 安装(一次性安装完整的 Boost)
如果你在某个平台上使用非缺省的编译器,如在 Windows 上或 macOS 上使用 GCC,一般就需要自己编译了,具体步骤请参见 Boost 的文档。不过,很多 Boost 库是完全不需要编译的,只需要把头文件加到编译器能找到的路径里就可以——如我们上一讲讨论的 Boost.Multiprecision 就是这样。我们讨论 Boost 库的时候,也会提一下使用这个库是否需要链接某个 Boost 库——需要的话,也就意味着需要编译和安装这个 Boost 库。
Boost.TypeIndex
TypeIndex 是一个很轻量级的库,它不需要链接,解决的也是使用模板时的一个常见问题,如何精确地知道一个表达式或变量的类型。我们还是看一个例子:
#include <iostream>
#include <typeinfo>
#include <utility>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id;
using boost::typeindex::
type_id_with_cvr;
int main()
{
vector<int> v;
auto it = v.cbegin();
cout << "*** Using typeid\n";
cout << typeid(const int).name()
<< endl;
cout << typeid(v).name() << endl;
cout << typeid(it).name() << endl;
cout << "*** Using type_id\n";
cout << type_id<const int>() << endl;
cout << type_id<decltype(v)>()
<< endl;
cout << type_id<decltype(it)>()
<< endl;
cout << "*** Using "
"type_id_with_cvr\n";
cout
<< type_id_with_cvr<const int>()
<< endl;
cout << type_id_with_cvr<decltype(
(v))>()
<< endl;
cout << type_id_with_cvr<decltype(
move((v)))>()
<< endl;
cout << type_id_with_cvr<decltype(
(it))>()
<< endl;
}
上面