the std::map
value is a heavy object consuming a lot of resources. Inserting such element into std::map
constraints one unnecessary
copy. How to eliminate these unneeded copy?
You are right about this and std::map::emplace
is the only solution what we have available now. A small explanation about the use of std::map::emplace
from cppreference.com is given below:
Careful use of emplace
allows the new element to be constructed while
avoiding unnecessary copy or move operations. The constructor of the
new element (i.e. std::pair<const Key, T>
) is called with exactly the
same arguments as supplied to emplace, forwarded via
std::forward<Args>(args)....
That means the a class-instances can be constructed in-place at the time of map insert(i.e, instead of constructing and copying), via std::map::emplace
, when you provide a contractor in the class with exactly same arguments. And then, you need to use std::map::emplace
along with std::piecewise_construct
and std::forward_as_tuple
(assuming that the class contains more than one member).
myMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(/*key of map*/),
std::forward_as_tuple(/*all the members which you want to constrct in place*/)
);
To demonstrate the above case, I have made a small sample code in which the members of ClassA
will be constructed in place without calling any of the special member functions. To make it sure, I have disabled default
, copy
and move
constructors.
SEE LIVE HERE
#include <iostream>
#include <map>
#include <tuple>
class ClassA
{
int _val;
std::string _str;
public:
explicit ClassA(const int val, const std::string& str) : _val(val), _str(str)
{
std::cout << "A class: C'tor called...!
";
}
// disable the following
ClassA() = delete;
ClassA(const ClassA&) = delete;
ClassA& operator=(const ClassA&) = delete;
ClassA(ClassA&&) = delete;
ClassA& operator=(ClassA&&) = delete;
};
class ClassB
{
ClassA _aObj;
public:
explicit ClassB(const int val, const std::string& str) : _aObj(val, str)
{
std::cout << "B class: C'tor called...!
";
}
// disable the following
ClassB() = delete;
ClassB(const ClassB&) = delete;
ClassB& operator=(const ClassB&) = delete;
ClassB(ClassB&&) = delete;
ClassB& operator=(ClassB&&) = delete;
};
int main()
{
std::map<int, ClassB> myMap;
myMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(1, "some string")
);
return 0;
}
Output:
A class: C'tor called...!
B class: C'tor called...!
Update: On the other hand,
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.
That means, your expectation(or assumption) of:
" I suspect some method in std::map
which inserts a new empty element with some key but no value, without calling any constructor for the value. Then I can move the value to thereby my own way. "
is not possible to achieve, by above mentioned std::map::emplace
way. As @aschepler pointed out in the comments, you can have maps with sometimes(optional) keys and no values by using the C++17 feature std::optional
.
For that, you need to make the values as optional as follows and of course the compiler version support C++17 or later.
std::map<Key, std::optional<Value>> myMap;
Now you can construct an object anytime in the code and after a while, you can move it to appropriate key-value(i.e, entry). Last but not least, don't forget to provide default move-c'ntors
SEE SAMPLE CODE HERE
#include <optional>
class ClassA {
/* same as before*/
public:
// disable the copy c'tor
// enable move c'ntors
ClassA(ClassA&&) = default;
ClassA& operator=(ClassA&&) = default;
};
class ClassB {
/* same as before*/
public:
// disable the copy c'tor
// enable move c'ntors
ClassB(ClassB&&) = default;
ClassB& operator=(ClassB&&) = default;
};
int main() {
std::map<int, std::optional<ClassB>> myMap;
// created without calling any constructors
myMap.emplace(1, std::nullopt);
//later in the code
ClassB bObj{1, "JeJo"};
// again after..... move it to the required key-value
myMap[1] = std::move(bObj);
return 0;
}
Output:
A class: C'tor called...!
B class: C'tor called...!