想象一下struct T:
struct T {
int i1, i2;
// no default constructor
explicit T(int i1, int i2): i1(i1), i2(i2) { }
};
使用默认构造函数非常简单:
aMap[123] = T(1, 23);
operator[] 允许按需创建一个不存在的条目(但为此它需要映射类型的默认构造函数)。
如果mapped_type 的类不提供默认构造函数,则可以通过std::unordered_map::find() 和std::unordered_map::insert() 的简单组合来匹配OP 的意图(或者仅通过后者检查成功)。
(这部分后来被插入,因为 A Lightness Races in Orbit 指出我跳过了这个简单的解决方案,直接转向更复杂的解决方案。)他为此写了alternative answer。由于它缺乏示范 MCVE,所以我拿走了我的并对其进行了改编:
#include <iostream>
#include <unordered_map>
struct T {
int i1, i2;
// no default constructor
explicit T(int i1, int i2): i1(i1), i2(i2)
{
std::cout << "T::T(" << i1 << ", " << i2 << ")\n";
}
};
int main()
{
typedef std::unordered_map<int, T> Map;
Map aMap;
//aMap[123] = T(1, 23); doesn't work without default constructor.
for (int i = 0; i < 2; ++i) {
Map::key_type key = 123;
Map::iterator iter = aMap.find(key);
if (iter == aMap.end()) {
std::pair<Map::iterator, bool> ret
= aMap.insert(Map::value_type(key, T(1 + i, 23)));
if (ret.second) std::cout << "Insertion done.\n";
else std::cout << "Insertion failed! Key " << key << " already there.\n";
} else {
std::cout << "Key " << key << " found.\n";
}
}
for (const auto &entry : aMap) {
std::cout << entry.first << " -> (" << entry.second.i1 << ", " << entry.second.i2 << ")\n";
}
return 0;
}
输出:
T::T(1, 23)
Insertion done.
Key 123 found.
123 -> (1, 23)
Live Demo on coliru
如果映射类型也确实缺少复制构造函数,那么它仍然可以使用 std::unordered_map::emplace() 解决(再次使用或不使用 std::unordered_map::find() 进行预检查):
aMap.emplace(std::piecewise_construct,
std::forward_as_tuple(123),
std::forward_as_tuple(1, 23));
改编样本:
#include <iostream>
#include <unordered_map>
struct T {
int i1, i2;
// no default constructor
explicit T(int i1, int i2): i1(i1), i2(i2)
{
std::cout << "T::T(" << i1 << ", " << i2 << ")\n";
}
// copy constructor and copy assignment disabled
T(const T&) = delete;
T& operator=(const T&);
};
int main()
{
typedef std::unordered_map<int, T> Map;
Map aMap;
for (int i = 0; i < 2; ++i) {
Map::key_type key = 123;
Map::iterator iter = aMap.find(key);
if (iter == aMap.end()) {
std::pair<Map::iterator, bool> ret
= aMap.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(1 + i, 23));
if (ret.second) std::cout << "Insertion done.\n";
else std::cout << "Insertion failed! Key " << key << " already there.\n";
} else {
std::cout << "Key " << key << " found.\n";
}
}
for (const auto &entry : aMap) {
std::cout << entry.first << " -> (" << entry.second.i1 << ", " << entry.second.i2 << ")\n";
}
return 0;
}
输出:
T::T(1, 23)
Insertion done.
Key 123 found.
123 -> (1, 23)
Live Demo on coliru
正如 Aconcagua 在评论中提到的,如果没有预先检查 find(),即使插入失败,emplace() 也可能会构造映射值。
文档。 cppreference 上的`std::unordered_map::emplace() 提到了这一点:
即使容器中已经有一个带有key的元素,也可以构造该元素,在这种情况下,新构造的元素将被立即销毁。
正如 Jarod42 提到的,std::unordered_map::try_emplace() 是 C++17 中值得一提的替代方案
与 insert 或 emplace 不同,如果插入未发生,这些函数不会从右值参数移动,这使得操作值是仅移动类型的映射变得容易,例如 std::unordered_map<std::string, std::unique_ptr<foo>>。另外try_emplace将mapped_type的key和参数分开处理,不像emplace那样需要参数来构造一个value_type(也就是一个std::pair)