I have a std::map of objects whose instances are very expensive to construct. (In real life they require several accesses to a database.)
I want to access an element of the map, or create it if it doesn’t exist. This sounds like a job for std::map::insert, except that the expensive object is constructed unnecessarily and then thrown away if the element exists. To illustrate:
#include <iostream>
#include <map>
#include <string>
struct CexpensiveObject
{
CexpensiveObject(const char* args="default"):args_(args)
{
std::cout << "Constructor: CexpensiveObject(" << args << ")" << std::endl;
}
CexpensiveObject( const CexpensiveObject& other )
{
std::cout << "Copy Constructor: CexpensiveObject other.args_ = " << other.args_ << "." << std::endl;
args_ = other.args_;
}
~CexpensiveObject()
{
std::cout << "Destructor: CexpensiveObject args_ = " << args_ << "." << std::endl;
}
const char* args_;
};
// entry point
int main()
{
typedef std::map<std::string, CexpensiveObject> mymaptype;
mymaptype mymap;
std::pair<mymaptype::iterator, bool> insertionResult;
std::cout << "First insertion" << std::endl;
insertionResult = mymap.insert( mymaptype::value_type( "foobar", CexpensiveObject("first") ) );
std::cout << "Was it inserted? " << (insertionResult.second?"yes":"no") << std::endl;
std::cout << "Second insertion" << std::endl;
insertionResult = mymap.insert( mymaptype::value_type("foobar", CexpensiveObject("second") ) );
std::cout << "Was it inserted? " << (insertionResult.second?"yes":"no") << std::endl;
}
Results:
First insertion
Constructor: CexpensiveObject(first)
Copy Constructor: CexpensiveObject other.args_ = first.
Copy Constructor: CexpensiveObject other.args_ = first.
Destructor: CexpensiveObject args_ = first.
Destructor: CexpensiveObject args_ = first.
Was it inserted? yes
Second insertion
Constructor: CexpensiveObject(second)
Copy Constructor: CexpensiveObject other.args_ = second.
Destructor: CexpensiveObject args_ = second.
Destructor: CexpensiveObject args_ = second.
Was it inserted? no
Destructor: CexpensiveObject args_ = first.
There’s more copying and destroying than I expected, but critically an instance CexpensiveObject is constructed and then thrown away if an element with the same key exists in the ma.
Am I misusing std::map::insert, or do I have to use std::map::find to check whether an element with the same key exists before I instantiate a CexpensiveObject instance?
It’s constructed before you even get to
insert, when you callCexpensiveObject("second"). You’re passing in the extraneous object! (And then it’s copied as thevalue_typeis passed toinsert.)Instead of
insert, usefind. If you find the item at the desired key, then you’re finished. If not, then insert it.