Imagine a struct T:
struct T {
int i1, i2;
// no default constructor
explicit T(int i1, int i2): i1(i1), i2(i2) { }
};
With a default constructor it's quite easy:
aMap[123] = T(1, 23);
The operator[] grants that a non-existing entry is created on demand (but for this it needs the default constructor of the mapped type).
If the class of mapped_type doesn't provide a default constructor OP's intention can be matched by a simple combination of std::unordered_map::find() and std::unordered_map::insert() (or just only the latter with check of success).
(This part was inserted later as A Lightness Races in Orbit pointed out that I skipped this simple solution and directly moved to the more complicated.) He wrote an alternative answer concerning this. As it is lacking a demonstrational MCVE, I took mine and adapted it:
#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;
}
Output:
T::T(1, 23)
Insertion done.
Key 123 found.
123 -> (1, 23)
Live Demo on coliru
If the mapped type does lack a copy constructor as well then it's still solvable using std::unordered_map::emplace() (again with or without pre-check with std::unordered_map::find()):
aMap.emplace(std::piecewise_construct,
std::forward_as_tuple(123),
std::forward_as_tuple(1, 23));
The adapted sample:
#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;
}
Output:
T::T(1, 23)
Insertion done.
Key 123 found.
123 -> (1, 23)
Live Demo on coliru
As Aconcagua mentioned in comment, without the pre-checking find(), the emplace() might construct the mapped value even if the insertion will fail.
The doc. of `std::unordered_map::emplace() on cppreference mentions this:
The element may be constructed even if there already is an element with the key in the container, in which case the newly constructed element will be destroyed immediately.
As Jarod42 mentioned, std::unordered_map::try_emplace() is an alternative in C++17 worth to be mentioned as
Unlike insert or emplace, these functions do not move from rvalue arguments if the insertion does not happen, which makes it easy to manipulate maps whose values are move-only types, such as std::unordered_map<std::string, std::unique_ptr<foo>>. In addition, try_emplace treats the key and the arguments to the mapped_type separately, unlike emplace, which requires the arguments to construct a value_type (that is, a std::pair)
my_setis invalid. Do you mean to usestd::unordered_mapinstead?my_set[1] = MyClass(...);? This may involve a copy. If that's not intended it's a bit more complicated.std::unordered_map, then no there's no other way than to have a default constructor.operator []for maps, because if you make an error in your algorithm (retrieving a non existing key you thought existed) it will compile and run and you will not know why you get the wrong result.