C++11:为任意标准容器emplace默认构造对象并返回迭代器的通用实现
嘿,我完全懂你遇到的痛点——标准容器的emplace接口确实没insert那么统一,不同容器的返回值类型差异直接给通用模板添了麻烦。咱们来一步步搞定这个问题。
首先得明确核心差异:你用insert的通用版本之所以能 work,是因为所有标准容器在末尾插入单个元素的insert接口,都统一返回指向新元素的迭代器。但emplace就不一样了:
- 序列容器(比如
vector、list、deque)的emplace需要指定插入位置(比如end()),返回的是指向新元素的迭代器 - 关联/无序关联容器(比如
map、unordered_map、set)的emplace不需要指定位置,直接构造元素,返回的是std::pair<iterator, bool>——迭代器指向新元素(或已存在的等效元素),bool值表示插入是否成功
针对这个差异,我们可以用两种方式实现通用的emplace模板:
方案1:用SFINAE分重载(兼容C++11及以上)
通过编译期判断容器emplace的返回类型,选择对应的重载:
#include <type_traits> #include <utility> // 针对序列容器:emplace需要指定位置,返回迭代器 template<typename C> auto emplace(C& container) -> std::enable_if_t< !std::is_same_v<decltype(container.emplace()), std::pair<typename C::iterator, bool>>, typename C::iterator > { return container.emplace(container.end()); } // 针对关联/无序关联容器:emplace返回pair,取迭代器部分 template<typename C> auto emplace(C& container) -> std::enable_if_t< std::is_same_v<decltype(container.emplace()), std::pair<typename C::iterator, bool>>, typename C::iterator > { return container.emplace().first; }
方案2:用if constexpr简化(C++17及以上)
如果你的项目已经支持C++17,用编译期分支可以把逻辑写到一个模板里,代码更紧凑:
#include <type_traits> #include <utility> template<typename C> auto emplace(C& container) -> typename C::iterator { if constexpr (std::is_same_v<decltype(container.emplace()), std::pair<typename C::iterator, bool>>) { // 关联容器:直接调用emplace,取返回pair的迭代器部分 return container.emplace().first; } else { // 序列容器:指定end()位置调用emplace,直接返回迭代器 return container.emplace(container.end()); } }
测试验证
这两个版本都能兼容常见标准容器,比如:
#include <vector> #include <unordered_map> #include <list> #include <set> int main() { std::vector<int> vec; auto it_vec = emplace(vec); // 正确返回vector<int>::iterator std::unordered_map<int, int> umap; auto it_umap = emplace(umap); // 正确返回unordered_map<int,int>::iterator std::list<std::string> lst; auto it_lst = emplace(lst); // 正常工作 std::set<double> s; auto it_set = emplace(s); // 正常工作 }
小注意点
- 这个实现默认容器的
value_type是可以默认构造的,和你原来的insert版本逻辑一致 - 对于
forward_list这种特殊序列容器,它的插入接口是emplace_after,和常规序列容器不同,如果需要支持它,得单独写特化版本(不过你的insert版本也同样不支持它,所以属于额外扩展范畴)
内容的提问来源于stack exchange,提问作者Naomi
相关产品推荐
相关产品推荐

