In C++ suppose I have an unordered map defined as follows:
unordered_map<int, MyClass> my_map;
auto my_class = my_map[1];
In the above code if 1 is not present as key in my_map it will initialize MyClass with default constructor and return. But is there a way to use non-default constructor of MyClass for initialization?
You're right that operator[] needs the value type to be default-constructible.
insert does not:
std::unordered_map<int, MyClass> my_map;
// Populate the map here
// Get element with key "1", creating a new one
// from the given value if it doesn't already exist
auto result = my_map.insert({1, <your value here>});
This gives you a pair containing an iterator to the element (whether created new, or already present), and a boolean (telling you which was the case).
So:
auto& my_class = *result.first;
const bool was_inserted = result.second;
Now you can do whatever you like with this information. Often you won't even care about result.second and can just ignore it.
For more complex value types you can play around with emplace, which is like insert but, um, better. Say you really don't want the value to be constructed if it won't be used, and you have C++17:
auto result = my_map.try_emplace(1, <your value's ctor args here here>);
If you don't care (or don't have C++17):
auto result = my_map.emplace(1, <your value>);
This is still better than insert as it can move the value into the map, rather than copying it.
Ultimately, and if you don't even want to unnecessarily produce your ctor args, you can always just do a find first, but it's nice to try to avoid that, as the insertion operation itself will be doing a find too.
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)
[] implements get_add_if_missing. Semantically, an overhead-free implementation would be something like:
value_type& get_add_if_missing(key_type const& k, auto&& factory) {
auto b = bucket_for(k);
auto pos = pos_for(k, b);
if (pos == b.end()) {
return b.append(k, factory());
} else {
return *pos;
}
}
A full equivalent is not there on the API yet (as of C++17), so for now, you need to decide what suboptimality to have based on how expensive it is to creating a temporary value_type:
do an extra lookup (search then insert if missing)
extra temporary (insert/emplace always, covered well in other answers)
An extra lookup version is:
final itr = m.find(key);
if (itr == m.end()) {
// insert or emplace a new element with the constructor of your choice
}
The std::unordered_map article on cppreference should have enough usage examples for insert / emplace.
With a tree-based implementation (std::map) a zero overhead get_add_if_missing emulation is quite possible with lower_bound followed by a hint-enabled insert / emplace.
And finally the good news -- if you can accept Boost.Intrusive (a header-only library) as a dependency, you can build a truly zero-overhead get_add_if_missing (no temporaries or repeated hash calculation). An API of the hash map from there is sufficiently detailed for that.
Related
Here is a minimal example where an object of type WrapMap contains an unordered_map. The only thing that will change in the map is the values, not the length and not the keys.
However, I have found that each value passed into each pair is copied twice.
By using move, it seems to have reduced the number of copies by 1 (although the move doesn't show up in the output, so maybe I've done something wrong).
#include <iostream>
#include <unordered_map>
using std::cout;
struct MyStruct {
int x;
MyStruct(int x) : x(x) { cout << "Constructed " << this << " from " << x << "\n"; }
MyStruct(const MyStruct& from) : x(from.x) { cout << "Copied " << this << " from " << &from << "\n"; }
MyStruct(MyStruct&& from) : x(from.x) { cout << "Moved " << this << " from " << &from << "\n"; }
~MyStruct() { cout << "Destructed " << this << " from " << x << "\n"; }
};
struct WrapMap {
std::unordered_map<std::string, MyStruct>&& my_map;
WrapMap(std::unordered_map<std::string, MyStruct>&& kv)
: my_map(std::move(kv)) {
/*
// Just to make sure it inputs the values correctly
cout << "{";
for (auto it = my_map.begin(); it != my_map.end(); ++it) {
if (it != my_map.begin()) cout << ", ";
cout << it->first << ": MyStruct " << it->second.x;
}
cout << "}\n";
*/
}
};
int main() {
WrapMap object({
{"foo", 2},
// several pairs
});
}
Constructed 0x7ffd76fadbb8 from 2
Copied 0x2611c80 from 0x7ffd76fadbb8
{foo: MyStruct 2}
Destructed 0x7ffd76fadbb8 from 2
Destructed 0x2611c80 from 2
My assumption is that the long pointer points to const memory (just a guess) and so it has to copy each element from const memory to non-const memory.
I have attempted to use an initializer_list<pair<string, MyStruct>> but I couldn't convert it to an unordered_map.
std::unordered_map<std::string, MyStruct> object = { {"foo", 2} } calls the copy constructor for each value, it seems.
How can I make it so that each key is never copied (or at least minimise it?)
Related: Insert in unordered map calls constructor
emplace
You can use the emplace member function of unordered_map:
Inserts a new element into the container constructed in-place with the given args if there is no element with the key in the container.
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).... [...]
std::unordered_map<std::string, MyStruct> m;
m.emplace(std::make_pair("foo", 2));
C++17: try_emplace
As of C++17, you can also make use of try_emplace which allows retaining a given resource passed to it if the key already exists:
[...] 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) [...].
std::unordered_map<std::string, MyStruct> m;
m.try_emplace("foo", 2);
I'm having the following code, but after run the code, the result is empty, any ideas why the result is empty? the reference of result in function main was passed to myclass, I thought function addToResult will actually add data to result, and I'm expecting a map key = "test", value = "1": "1". I'm kind of new to c++. Thanks!
#include <iostream>
#include <string>
#include <unordered_map>
using LookUpTable = std::unordered_map<std::string, std::string>;
using DLTable = std::unordered_map<std::string, LookUpTable>;
class MyClass
{
public:
MyClass(DLTable& dltable) {
m_dltable = dltable;
};
void addToResult() {
LookUpTable ee;
ee.emplace("1", "1");
m_dltable.emplace("test", ee);
};
private:
DLTable m_dltable;
};
int main ()
{
DLTable result;
MyClass myclass(result);
myclass.addToResult();
std::cout << "myrecipe contains:" << std::endl;
for (auto& x: result) {
std::cout << x.first << ": "<< std::endl;
for (auto& xx : x.second) {
std::cout << xx.first << ": " << xx.second << std::endl;
}
}
std::cout << std::endl;
return 0;
}
Let' look into simplified example:
int a = 0;
int &b = a;
int c = b;
c = 123;
Will last assignment modify a? Of course not. It does not matter how you pass value to c through reference or not c is completely independent variable that just initialized by a reference.
Your case is the same - m_dltable is separate variable and the fact you initialize it using reference does not change anything. (Your case even worse, you did not initialize it by reference, you assigned to it)
In general your approach is wrong. If you want directly access that variable then just make it public, do not try to create convoluted workarounds on how to access it. If you want incapsulation just create members that allow you to iterate over that container. For example return a const reference to it or have begin() and end() methods that return (const) iterators accordingly.
Introduction
Hello everyone, i try to use boost::unordered_set for a custom class type. The class stores information about coordinates and several other values but only the coordinates are used to create the hash value. Now if i want to insert a point and there is already a point with equal coordinates (hence a set) i need to change a third value from the original object (like object.isDuplicate = true very simplified). Please do not stick too much to the bool value and duplicate detection cause in the original code it is a bit more complex but it should only show that i need a non-const access to the stored class. I can only use boost 1.53 and C++03 and GCC 4.4.3
The problem
The problem is now when i try to insert a point with boost::unordered_set::insert i get a pair<iterator, bool> of which the first member is an immutable iterator to the inserted or original entry and the second is a bool indicating if the value was inserted or not. I can not change the value with an immutable iterator unfortunately so i had to think of something different. So i now try to store a pointer to my object in the set and then access it via this pointer to change the value (which should be okay since the value has nothing to do with the hash value and thus does not alter the key). So i tried to overload the boost::hash_value function to accept a pointer to my class like this:
size_t hash_value(const A * a) {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
return seed;
}
But the unordered_set does not seem to use my overloaded function (i tried printing the seed at the end but it does not show up hence i assume it uses a different overload) even if i initialize my set with unordered_set< A *, boost::hash<A *> >. For the hashing aspect: when i try to use the set without a pointer it works fine but i can not alter the value.
Possible problem
I searched a bit around in the boost::hash reference and found this overload template<typename T> std::size_t hash_value(T* const&); which i think is used instead of my own one (and simply hashes with the objects address) but then i wonder why my compiler does not prompt a redefinition of this function (i compile with -Wall -Wextra -pedantic flags enabled.
Question
So is this the actual problem? And if it is how can i tell my compiler to explicitely use my custom hash function?
Code
At last a little example i wrote to test everything
#include <iostream>
#include <string>
#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
using boost::unordered_set;
struct A {
double a;
double b;
bool isDup;
A(const double a, const double b): a(a), b(b), isDup(false) {}
A(const A & a): a(a.a), b(a.b), isDup(a.isDup) {}
/* Two equal As ought to have a bitwise equal floating point value so this is okay */
bool operator==(const A & a) const {
if (a.a != this->a) return false;
if (a.b != this->b) return false;
return true;
}
};
size_t hash_value(const A * a) {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
std::cout << "Seed: " << seed << std::endl; /* This is not printed so i assume the function is not called */
return seed;
}
int main() {
A a1(1.2, 2.3);
A a2(2.3, 3.4);
A a3(3.4, 4.5);
A a4(a1);
unordered_set< A *, boost::hash<A *> > usa; /* This was unintended lol */
if ( ! usa.insert(&a1).second ) std::cout << "Error " << a1.a << ", " << a1.b << " is already in set" << std::endl;
if ( ! usa.insert(&a2).second ) std::cout << "Error " << a2.a << ", " << a2.b << " is already in set" << std::endl;
if ( ! usa.insert(&a3).second ) std::cout << "Error " << a3.a << ", " << a3.b << " is already in set" << std::endl;
if ( ! usa.insert(&a4).second ) {
/* This is not called */
std::cout << "Error " << a4.a << ", " << a4.b << " is already in set" << std::endl;
(*(usa.insert(&a4).first))->isDup = true;
}
}
There are a couple of issues with your original function hash_value:
It must be inside boost namespace because boost::hash<T*> invokes boost::hash_value which disables argument-dependent name lookup.
In templates name lookup is performed twice: at declaration and instantiation time. At instantiation time only argument-dependent name lookup is performed but it is disabled by 1. This is why your hash function must be declared before the definition of boost::hash (before including boost/hash.hpp).
E.g.:
#include <cstddef> // std::size_t
struct A;
namespace boost { inline std::size_t hash_value(A* a); }
#include <iostream>
#include <string>
#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
struct A { /*... */};
size_t boost::hash_value(A* a) {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
std::cout << "Seed: " << seed << std::endl; /* This is not printed so i assume the function is not called */
return seed;
}
Also, you need to specify your own element comparison class, the default one in boost::unordered_set compares pointers.
As a side note the design of boost::hash and std::hash is less than ideal in respect of combining hashes of multiple members. I cannot recommend enough using the new hash framework from N3980 Types Don't Know #.
Okay i found a solution (or a workaround rather?) by myself now. A second problem was the equal_to class which is used by default by boost::unordered_set. equal_to<A *> would never return false because we always have distinct points and thus &a1 == &a2 would always evaluate to false so i had to write my own comparator as well which dereferences the objects before comparing them and then invoces their operator==.
Then I simply encapsulated the hash function and the comparator in a separate class and then pass them as template arguments when creating the set like this:
class compA {
public:
size_t operator()(const A * a) const {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
return seed;
}
bool operator()(const A * a1, const A * a2) const {
if (*a1 == *a2) return true;
return false;
}
};
unordered_set<A *, compA, compA> usa;
But i still would like to know why my initial attempt did not work.
I have a class with a std::vector<int> member and a member function returning a const reference to that vector.
class demo {
public:
//...
const std::vector<int> & test() const {
return iv;
}
private:
std::vector<int> iv;
};
I plan to change the member type to a different array like container type with just enough functionality and a smaller memory footprint (e.g. std::experimental::dynarray, std::unique_ptr<int[]>). Therefore I thought it would be a good idea to not return the real container as a const reference but to return a view to the elements as a gsl::span<const int>.
class demo {
public:
//...
gsl::span<const int> test() const {
return iv;
}
private:
std::vector<int> iv;
};
But this breaks code that worked with the const vector<int>& because two span instances of the same unmodified vector can't be used to iterate over the elements:
demo d;
std::cout << (d.test().begin() == d.test().begin()) << "\n";
std::cout << (d.test().end() == d.test().end()) << "\n";
for( auto it = d.test().begin(), end = d.test().end(); it != end; ++it )
std::cout << *it << "\n";
This prints 0 0 and then crashes because the test it != end never fails.
A range based for loop works of course, but this loop is valid and therefore must also work as expected.
I had expected, that all spans from the same range of the same container are equal so that iterators of any of these spans are comparable (container not modified of course). Certainly there is a good reason why this isn't so.
So my question is, what is the best way to return such a view to elements of a array like container whose type should not be visible to the caller.
You use iterator of temporary, so your iterator become invalided directly after affectation.
You may use the following:
auto&& view = d.test();
for (auto it = view.begin(), end = view.end(); it != end; ++it) {
std::cout << *it << "\n";
}
So the answer, is NO, because it breaks code, that was valid before.
I'm implementing an STL set with a complex template parameter type. When inserting in to the set, I want the set to use the less-than operator I've defined for my type. I also want to minimize the quantity of object instantiations of my type. It seems I can't have both.
I've got two minimal examples below, each uses the same C++ class.
#include <iostream>
#include <set>
using namespace std;
class Foo {
public:
Foo(int z);
Foo(const Foo &z);
bool operator<(const Foo &rhs) const;
int a;
};
Foo::Foo(int z)
{
cout << "cons" << endl;
a = z;
}
Foo::Foo(const Foo &z)
{
cout << "copy cons" << endl;
a = z.a;
}
bool
Foo::operator<(const Foo &rhs) const
{
cout << "less than" << endl;
return a < rhs.a;
}
Here's my first main():
int
main(void)
{
set<Foo> s;
s.insert(*new Foo(1));
s.insert(*new Foo(2));
s.insert(*new Foo(1));
cout << "size: " << s.size() << endl;
return 0;
}
That's great because it uses the less-than I've defined for my class, and thus the size of the set is correctly two. But it's bad because every insertion in to the set requires the instantiation of two objects (constructor, copy constructor).
$ ./a.out
cons
copy cons
cons
less than
less than
less than
copy cons
cons
less than
less than
less than
size: 2
Here's my second main():
int
main(void)
{
set<Foo *> s;
s.insert(new Foo(1));
s.insert(new Foo(2));
s.insert(new Foo(1));
cout << "size: " << s.size() << endl;
return 0;
}
That's great because an insertion requires just one object instantiation. But it's bad because it's really a set of pointers, and thus the uniqueness of set members is gone as far as my type is concerned.
$ ./a.out
cons
cons
cons
size: 3
I'm hoping there's some bit of information I'm missing. Is it possible for me to have both minimal object instantiations and appropriate sorting?
You are getting a copy from this: *new Foo(1).
Create this struct:
template<typename T>
struct PtrLess
{
bool operator()(const T *a, const T *b) const
{
return *a < *b;
}
};
Make the map look like set<Foo*, PtrLess<Foo>> s; and then add Foo's like s.insert(new Foo(1));
Note the *
Otherwise, when the map creates a container for the Foo item, since it is allocated within the foo containers definition, the map has to copy the supplied value into its internal Foo object.
Standard containers store a copy of the items that are added. If you want your set to store objects, rather than pointers you should simply do the following, otherwise you're creating a memory leak, since the objects allocated via new are never free'd via a corresponding delete.
int main()
{
set<Foo> s;
s.insert(Foo(1));
s.insert(Foo(2));
s.insert(Foo(1));
cout << "size: " << s.size() << endl;
return 0;
}
If you want to minimise the number of temporary objects instantiated, just use a single temporary:
int main()
{
set<Foo> s;
Foo temp(1);
s.insert(temp);
temp.a = 2;
s.insert(temp);
temp.a = 1;
s.insert(temp);
cout << "size: " << s.size() << endl;
return 0;
}
The output for this snippet (via ideone) is:
cons
copy cons
less than
less than
less than
copy cons
less than
less than
less than
size: 2
Generally, I would prefer to store the actual objects in a set<Foo> rather than pointers to objects in a set<Foo*>, since there can be no problems with object ownership (who/when new and delete need to be called), the total amount of memory allocated is smaller (for N items you need N*sizeof(Foo) rather than N*(sizeof(Foo) + sizeof(Foo*)) bytes) and data access could typically be expected to be faster (since there's no extra pointer indirection).
Hope this helps.
This is an extension to #Mranz's answer. Instead of dealing with raw pointers, put the pointers in an std::unique_ptr
#include <memory>
using namespace std;
template<typename T>
struct PtrLess
{
bool operator()(const T& a, const T& b) const
{
return *a < *b;
}
};
int
main(void)
{
set<unique_ptr<Foo>, PtrLess<unique_ptr<Foo>>> s;
s.insert(unique_ptr<Foo>(new Foo(1)));
s.insert(unique_ptr<Foo>(new Foo(2)));
s.insert(unique_ptr<Foo>(new Foo(1)));
cout << "size: " << s.size() << endl;
return 0;
}