std::map的insert()重载解析:如何指定调用const左值引用版本?
关于std::map insert重载的疑问与解答
问题背景
我正在实现一个镜像std::map接口的模板库,在处理以下insert()重载时遇到疑问:
iterator insert( const value_type& value ); template< class P > iterator insert( P&& value );
根据cppreference的说明,第二个重载等价于emplace(std::forward<P>(value)),且仅当std::is_constructible<value_type, P&&>::value == true时才会参与重载解析。
我测试了多种类型,这个条件都返回true,想知道:
- 何时该条件会为false?
- 如何明确调用第一个
const左值引用的重载?
我的测试代码及输出如下:
std::map<std::string,int> map; std::cout << ((std::is_constructible<std::map<std::string,int>::value_type, const std::pair<const std::string,int>&&>::value)? "yes" : "no") << ((std::is_constructible<std::map<std::string,int>::value_type, const std::pair<std::string,int>&&>::value)? "yes" : "no") << ((std::is_constructible<std::map<std::string,int>::value_type, std::pair<const std::string,int>&&>::value)? "yes" : "no") << ((std::is_constructible<std::map<std::string,int>::value_type, std::pair<std::string,int>&&>::value)? "yes" : "no") << std::endl;
输出:
yesyesyesyes
一、何时std::is_constructible<value_type, P&&>::value为false
你测试的所有类型都能通过隐式或显式转换构造出std::map的value_type(即std::pair<const Key, T>),所以条件均为true。要让条件不满足,只需传入无法构造出value_type的类型,比如:
- 完全无关的基础类型:比如
int,std::is_constructible<std::map<std::string,int>::value_type, int&&>::value会返回false,因为无法用int右值构造std::pair<const std::string, int>。 - 类型不匹配的兼容结构:比如
std::pair<int, std::string>,虽然是pair,但键值类型顺序和value_type相反,无法直接构造目标pair,因此构造条件为false。 - 无适配转换的自定义类型:如果自定义类型
X没有任何能转换为value_type的构造函数或转换运算符,那么std::is_constructible<value_type, X&&>::value也会是false。
二、如何明确调用第一个const value_type&重载
有几种可靠方式强制编译器选择左值引用版本:
- 用
std::as_const转换为const左值
将传入对象转为const左值后,编译器会优先匹配非模板的const value_type&重载(模板重载需要类型推导,优先级低于非模板匹配):std::pair<const std::string, int> val("test", 100); map.insert(std::as_const(val)); - 显式构造
value_type对象传入
如果原本传入的是可构造value_type的其他类型(比如std::pair<std::string, int>),先构造出value_type实例再传入:std::pair<std::string, int> temp("test", 200); map.insert(static_cast<std::map<std::string,int>::value_type>(temp)); - 显式指定模板参数(写法生硬,不推荐)
直接为模板重载指定const value_type&作为模板参数P,让其匹配第一个重载的签名:std::pair<const std::string, int> val("test", 300); map.insert<const std::map<std::string,int>::value_type&>(val);
内容的提问来源于stack exchange,提问作者chiasmos
相关产品推荐
相关产品推荐

