C++20如何通过concept约束模板参数兼容map与unordered_map等关联容器
问题解答
1. 能否编写接受所有map类实现的C++20函数?
完全可以。C的模板本质是基于行为匹配的鸭子类型,配合C20引入的concept语法,你完全可以写出和具体map实现无关的通用函数,不需要依赖共同基类。
2. 如何定义限制map行为的C++20 concept?
不需要把完整的Map接口全部翻译成concept,你只需要把实际用到的操作写到concept约束里就可以了——C++的设计原则是你需要什么就约束什么,不需要为了匹配名义上的接口去枚举所有无关操作。
举个例子,如果你需要的是键为std::string、值为int的映射类型,需要支持按key取值、遍历、插入这几个常用操作,对应的concept可以这么写:
#include <concepts> #include <string> #include <utility> template<typename T> concept StringIntMap = requires(T m, const T cm, std::string key, int value) { // 要求存在key_type、mapped_type嵌套类型,分别对应string和int typename T::key_type; typename T::mapped_type; std::same_as<typename T::key_type, std::string>; std::same_as<typename T::mapped_type, int>; // 要求支持[]运算符按key读写值 { m[key] } -> std::same_as<int&>; // 要求支持at方法按key读值 { cm.at(key) } -> std::same_as<const int&>; // 要求支持insert插入键值对 { m.insert(std::make_pair(key, value)) }; // 要求支持范围遍历,有合法的begin/end迭代器 { cm.begin() } -> std::input_iterator; { cm.end() } -> std::input_iterator; // 要求支持size方法获取元素个数 { cm.size() } -> std::convertible_to<std::size_t>; // 要求支持count方法判断key是否存在 { cm.count(key) } -> std::convertible_to<std::size_t>; };
这个concept已经可以同时匹配std::map<std::string, int>、std::unordered_map<std::string, int>以及后续所有满足这些操作的第三方map实现,你写函数的时候直接用这个concept约束参数即可:
// 通用的map处理函数 void process_map(StringIntMap auto& map) { map["test"] = 123; for (auto& [k, v] : map) { // 自定义遍历逻辑 } }
如果需要更通用的、支持任意键值类型的map concept,稍作修改即可实现:
template<typename T, typename Key, typename Value> concept Map = requires(T m, const T cm, Key key, Value value) { typename T::key_type; typename T::mapped_type; std::same_as<typename T::key_type, Key>; std::same_as<typename T::mapped_type, Value>; { m[key] } -> std::same_as<Value&>; { cm.at(key) } -> std::same_as<const Value&>; { m.insert(std::pair<Key, Value>(key, value)) }; { cm.begin() } -> std::input_iterator; { cm.end() } -> std::input_iterator; { cm.size() } -> std::convertible_to<std::size_t>; { cm.count(key) } -> std::convertible_to<std::size_t>; }; // 使用示例:约束键为std::string,值为int的map void process_map(Map<std::string, int> auto& map) { // 业务逻辑 }
补充说明
C标准库本身没有提供内置的通用Map concept,因为不同场景下对map的行为要求差异极大:有的场景需要有序性,有的不需要;有的场景需要支持异构查找,有的不需要。你完全可以根据自己的业务需求裁剪concept里的约束项,不需要兼容所有用不到的接口,这也是C泛型比Java接口更灵活的地方。
内容的提问来源于stack exchange,提问作者pasaba por aqui
相关产品推荐
相关产品推荐

