13.5. Boost.Bimap

Boost.Bimap 库提供了一个建立在 Boost.MultiIndex 之上但不需要预先定义就可以使用的容器。 这个容器十分类似于 std::map, 但他不仅可以通过 key 搜索, 还可以用 value 来搜索。

  1. #include <boost/bimap.hpp>
  2. #include <iostream>
  3. #include <string>
  4.  
  5. int main()
  6. {
  7. typedef boost::bimap<std::string, int> bimap;
  8. bimap persons;
  9.  
  10. persons.insert(bimap::value_type("Boris", 31));
  11. persons.insert(bimap::value_type("Anton", 31));
  12. persons.insert(bimap::value_type("Caesar", 25));
  13.  
  14. std::cout << persons.left.count("Boris") << std::endl;
  15. std::cout << persons.right.count(31) << std::endl;
  16. }

boost/bimap.hpp 中定义的 boost::bimap 为我们提供了两个属性: leftright 来访问在 boost::bimap 统一的两个 std::map 类型的容器。 在例子中, leftstd::string 类型的 key 来访问容器, 而 right 用到了 int 类型的 key。

除了支持用 left 和 right 对容器中的记录进行单独的访问, boost::bimap 还允许像下面的例子一样展示记录间的关联关系。

  1. #include <boost/bimap.hpp>
  2. #include <iostream>
  3. #include <string>
  4.  
  5. int main()
  6. {
  7. typedef boost::bimap<std::string, int> bimap;
  8. bimap persons;
  9.  
  10. persons.insert(bimap::value_type("Boris", 31));
  11. persons.insert(bimap::value_type("Anton", 31));
  12. persons.insert(bimap::value_type("Caesar", 25));
  13.  
  14. for (bimap::iterator it = persons.begin(); it != persons.end(); ++it)
  15. std::cout << it->left << " is " << it->right << " years old." << std::endl;
  16. }

对一个记录访问时, leftright 并不是必须的。 你也可以使用迭代器来访问每个记录中的 left 和 right 容器。

std::mapstd::multimap 组合让你觉得似乎可以存储多个具有相同 key 值的记录, 但 boost::bimap 并没有这样做。 但这并不代表在 boost::bimap 存储两个具有相同 key 值的记录是不可能的。 严格来说, 那两个模板参数并不会对 leftright 的容器类型做出具体的规定。 如果像例子中那样并没有指定容器类型时, boost::bimaps::set_of 类型会缺省的使用。 跟 std::map 一样, 它要求记录有唯一的 key 值。

第一个 boost::bimap 例子也可以像下面这样写。

  1. #include <boost/bimap.hpp>
  2. #include <iostream>
  3. #include <string>
  4.  
  5. int main()
  6. {
  7. typedef boost::bimap<boost::bimaps::set_of<std::string>, boost::bimaps::set_of<int>> bimap;
  8. bimap persons;
  9.  
  10. persons.insert(bimap::value_type("Boris", 31));
  11. persons.insert(bimap::value_type("Anton", 31));
  12. persons.insert(bimap::value_type("Caesar", 25));
  13.  
  14. std::cout << persons.left.count("Boris") << std::endl;
  15. std::cout << persons.right.count(31) << std::endl;
  16. }

除了 boost::bimaps::set_of, 你还可以用一些其他的容器类型来定制你的 boost::bimap

  1. #include <boost/bimap.hpp>
  2. #include <boost/bimap/multiset_of.hpp>
  3. #include <iostream>
  4. #include <string>
  5.  
  6. int main()
  7. {
  8. typedef boost::bimap<boost::bimaps::set_of<std::string>, boost::bimaps::multiset_of<int>> bimap;
  9. bimap persons;
  10.  
  11. persons.insert(bimap::value_type("Boris", 31));
  12. persons.insert(bimap::value_type("Anton", 31));
  13. persons.insert(bimap::value_type("Caesar", 25));
  14.  
  15. std::cout << persons.left.count("Boris") << std::endl;
  16. std::cout << persons.right.count(31) << std::endl;
  17. }

代码中的容器使用了定义在 boost/bimap/multiset_of.hpp 中的 boost::bimaps::multiset_of。 这个容器的操作和 boost::bimaps::set_of 差不了多少, 只是它不再要求 key 值是唯一的。 因此, 上面的例子将会在计算 age 为 31 的 person 数时输出: 2

既然 boost::bimaps::set_of 会在定义 boost::bimap 被缺省的使用, 你没必要再显示的包含头文件: boost/bimap/set_of.hpp。 但在使用其它类型的容器时, 你就必须要显示的包含一些相应的头文件了。

Boost.Bimap 还提供了类: boost::bimaps::unordered_set_ofboost::bimaps::unordered_multiset_ofboost::bimaps::list_ofboost::bimaps::vector_ofboost::bimaps::unconstrainted_set_of 以供使用。 除了 boost::bimaps::unconstrainted_set_of, 剩下的所有容器类型的使用方法都和他们在 C++ 标准里的版本一样。

  1. #include <boost/bimap.hpp>
  2. #include <boost/bimap/unconstrained_set_of.hpp>
  3. #include <boost/bimap/support/lambda.hpp>
  4. #include <iostream>
  5. #include <string>
  6.  
  7. int main()
  8. {
  9. typedef boost::bimap<std::string, boost::bimaps::unconstrained_set_of<int>> bimap;
  10. bimap persons;
  11.  
  12. persons.insert(bimap::value_type("Boris", 31));
  13. persons.insert(bimap::value_type("Anton", 31));
  14. persons.insert(bimap::value_type("Caesar", 25));
  15.  
  16. bimap::left_map::iterator it = persons.left.find("Boris");
  17. persons.left.modify_key(it, boost::bimaps::_key = "Doris");
  18.  
  19. std::cout << it->first << std::endl;
  20. }

boost::bimaps::unconstraintedset_of 可以使 boost::bimap 的 _right (也就是 age)值无法用来查找 person。 在这种特定的情况下, boost::bimap 可以被视为是一个 std::map 类型的容器。

虽然如此, 例子还是向我们展示了 boost::bimap 对于 std::map 的优越性。 因为 Boost.Bimap 是基于 Boost.MultiIndex 的, 你当然可以使用 Boost.MultiIndex 提供的所有函数。 例子中就用 modify_key() 修改了 key 值, 这在 std::map 中是不可能的。

请注意修改 key 值的以下细节: key 通过 boost::bimaps::_key 函数赋予了新值, 而 boost::bimaps::_key 是一个定义在 boost/bimap/support/lambda.hpp 中的 lambda 函数。 有关 lambda 函数, 详见:第 3 章 函数对象

boost/bimap/support/lambda.hpp 还定义了 boost::bimaps::_data。 函数 modify_data() 可以用来修改 boost::bimap 中的 value 值。