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.
Related
If possible, how could I use some type-specific function (such as .size() for: std::string or std::vector or ...) in a function with a template type, being sure that when I'll use that type-specific function I'm actually calling it with the correct type as argument? Maybe I'm wrong, and if it is, please explain to me what I have to do.
#include <iostream>
#include <string>
template <typename T>
std::string func(T& number) {
if (typeid(T) == typeid(std::string)) {
unsigned short int size = number.size();// !
return " is a string";
}
else if (typeid(T) == typeid(int)) {
return " is an int";
}
//...
}
int main() {
std::string name = "Anthony";
int age = 8;
std::cout << name /*<< func(name) */<< '\n' << age << func(age) << '.';
return 0;
}
I know that in the code above the line:
unsigned short int size = number.size();//(I need exactly '.size()')
doesn't make any sense (even the whole code doesn't make much sense) considering that I never use that value, but to find the size of the string (when it is a string!) is exactly what I need, and to not post a very long code that would make sense, I'm posting only this to make it give the error I've had when trying to compile, and in order to give you a minimal reproducible example. So please, don't say to me "just delete that line and your code will work").
Instead of if (typeid(T) == typeid(std::string)), use if constexpr (std::is_same_v<T, std::string>). ( Similarly, else if constexpr instead of else if).
Regular if requires both branches to be valid, even if the condition is known at compile-time. if constexpr requires a compile-time condition, but allows the discarded branch to be invalid (only if the error is related to the template argument; every branch has to be theoretically valid for some template argument).
std::is_same_v<T, std::string> is similar to typeid(T) == typeid(std::string), except it counts as a compile-time constant. if constexpr would reject the latter.
If you really need to use a template here, simply specialize the template.
template <typename T>
std::string func(T& number);
template<>
std::string func<std::string>(std::string& number) {
unsigned short int size = number.size();// !
return " is a string";
}
template<>
std::string func<int>(int& number) {
return " is an int";;
}
Usually you using a template you want to avoid using specific implementations for types though. Overloads would be preferrable for a limited set of types using type-specific implementations though.
Since your requirement is not restricted to std::string(as you have mentioned std::vector etc), you can use SFINAE as shown below:
#include <iostream>
#include<typeinfo>
#include<string>
#include<vector>
#include <type_traits>
//make sure that this overload is added to the set when T has a size() member function which is your requirement
template<typename T>
auto func(T const& number) -> decltype((void)(number.size()), std::string())
{
auto size = number.size();
return " is a " + std::string(typeid(number).name());
}
template<typename T>
std::enable_if_t<std::is_fundamental_v<std::remove_reference_t<T>>,std::string> func(T const& number)
{
return " is a " + std::string(typeid(number).name());
}
int main()
{
std::string name = "Anthony";
int age = 8;
double dage = 22.2;
std::cout << name << func(name) << '\n' << age << func(age) << '.'<<"\n"<<dage << func(dage);
//lets test it with vector
std::vector<int> vec= {1,2,3};
std::cout<<"\nvec: "<< func(vec);
return 0;
}
Demo
The output of the above program can be seen here:
Anthony is a NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
8 is a i.
22.2 is a d
vec: is a St6vectorIiSaIiEE
I have an Expression class that stores algebraic terms. The public interface I want it to have is something like:
expr.add({1}) // add term "x1". (error: code adds constant 1)
expr.add({0,1}) // add term "x0 * x1".
expr.add({0},2) // add term "2 * x0"
expr.add(2) // add constant 2
But I'm having the problem that expr.add({1}) is being interpreted as adding the integer 1, not adding the vector containing 1. Is there any way I can fix the implementation below to allow for the interface above? (Or at the very least catch it?) Because typing out expr.add(std::vector({1})) is far too verbose.
#include <tuple>
#include <vector>
#include <unordered_set>
#include <iostream>
class Expression
{
using var_t = unsigned int;
using term_t = std::pair<int, std::vector<var_t>>;
std::vector<term_t> terms;
public:
void add(const std::vector<var_t>& vars, int coeff=1)
{
std::cout << "Adding a term" << std::endl;
terms.push_back(std::make_pair(coeff, vars));
}
void add(int constant)
{
std::cout << "Adding a constant" << std::endl;
terms.push_back(std::make_pair(constant, std::vector<var_t>{}));
}
};
int main()
{
Expression expr;
expr.add({1},1); // "Adding a term"
expr.add({1}); // "Adding a constant"
expr.add(1); // "Adding a constant"
}
You can add an overload for initializer_list which will bind to the argument if it's in a brace initializer
void add(std::initializer_list<var_t> vars, int coeff=1)
{
add(std::vector<var_t>(vars), coeff);
}
In the implementation, you can just call the vector overload explicitly and/or give the user a warning.
Here's a demo.
You can change the parameter type to std::initializer_list, which would be preferred.
void add(std::initializer_list<var_t> vars, int coeff=1)
{
std::cout << "Adding a term" << std::endl;
terms.push_back(std::make_pair(coeff, std::vector<var_t>(vars)));
}
LIVE
You may change it to initializer_list in the arguments.
void add(std::initializer_list<var_t> vars, int coeff=1)
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.
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.
I have classes/structs with member functions. I want to check that the return type has the correct size.
#include <iostream>
#include <type_traits>
struct QString {
wchar_t ucs2() {return L"1"[0];}
};
int main() {
QString arg;
std::cout << "wchar_t " << sizeof(wchar_t) << std::endl;
std::cout << "std::invoke_result_t<decltype(&QString::ucs2)(void)> " << sizeof(std::invoke_result_t<decltype(&QString::ucs2)(void)>) << std::endl;
}
Result is, for example,
wchar_t 4
std::invoke_result_t<decltype(&QString::ucs2)(void)> 16
so I get different sizes. I expected std::invoke_result_t<decltype(&QString::ucs2)(void) to be the return type of my ucs2 function, i.e. wchar_t, but it doesn't seem to be the case. What is going on here?
invoke_result is defined as:
template< class F, class... ArgTypes>
class invoke_result;
Hence, this is working
return sizeof(std::invoke_result_t<decltype(&QString::ucs2), QString>);
A simpler solution:
int main() {
std::cout << sizeof(QString().ucs2()) << '\n';
return 0;
}
The QString() default constructs a QString, which can then be used to call ucs2. But since this all happens as an operand to sizeof, nothing is actually constructed or called. The compiler does all the type analysis at compile time, even without enabling optimizations.
Obviously, if you can't default construct the class, this may be less useful.