Reputation: 189
I want to construct a vector that will hold int M
elements of my class Point
which represent a single point (x,y) where both x,y are int
.
The points that will be stored in this vector are randomly generated. However, I do not want to store the same point more than once.
My implementation is the following:
#include <algorithm>
#include <random>
std::random_device rd;
std::mt19937 mt(rd());
std::vector<Point> InitList(int M)
{
if ( M <=0 )
{
std::cout << "InitList: Length of vector must be greater than zero. Don't play around!"
<< std::endl;
exit(EXIT_FAILURE);
}
Point to_erase(-100,-100); // <----- can I get rid of this?
Point to_add;
int x,y;
std::vector<Point> vec = {};
vec.push_back(to_erase); // <----- and this
while (vec.size() < M+1) // <----- and then M+1 -> M
{
std::uniform_int_distribution<int> RandInt(0,100);
x = RandInt(mt);
y = RandInt(mt);
point.set(x,y);
if ( std::find(vec.begin(), vec.end(),to_add) == vec.end() )
vec.push_back(to_add);
}
vec.erase (vec.begin()); // <----- and finally avoid this?
return vec;
}
How can I avoid the trick of adding the first instance to_erase
?
EDIT
So I changed the implementation to use std::unordered_set
and I am getting the following errors:
error: use of deleted function ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set() [with _Value = Site; _Hash = std::hash<Site>; _Pred = std::equal_to<Site>; _Alloc = std::allocator<Site>]’
note: ‘std::hash<Site>::hash()’ is implicitly deleted because the default definition would be ill-formed:
101 | struct hash : __hash_enum<_Tp>
| ^~~~
/usr/include/c++/9/bits/functional_hash.h:101:12: error: no matching function for call to ‘std::__hash_enum<Site, false>::__hash_enum()’
/usr/include/c++/9/bits/functional_hash.h:82:7: note: candidate: ‘std::__hash_enum<_Tp, <anonymous> >::__hash_enum(std::__hash_enum<_Tp, <anonymous> >&&) [with _Tp = Site; bool <anonymous> = false]’
82 | __hash_enum(__hash_enum&&);
| ^~~~~~~~~~~
And the list goes on. I guess there is an implementation missing in my class Point
. What should I do?
Upvotes: 0
Views: 192
Reputation: 15946
You can guarantee uniqueness just by using std::set
(https://www.cplusplus.com/reference/set/set/) instead of std::vector
.
If order doesn't matter, use std::unordered_set
.
Regarding the hash
issues you're seeing -- you have to define a hash function for a custom type, when using a few C++ types like set
, unordered_set
, map
, etc. This is a function that returns a unique number based on the value of your custom Point
instance. I stubbed out this example to give you an idea of how this might work for you, but YMMV:
#include <iostream>
#include <unordered_set>
struct Point
{
int x;
int y;
Point(int x, int y): x(x), y(y) {};
};
bool operator==(const Point& lhs, const Point& rhs)
{
return ((lhs.x == rhs.x) and (lhs.y == rhs.y));
}
namespace std
{
template <>
struct hash<Point>
{
size_t operator()( const Point& p ) const
{
return (p.x << 32 | p.y);
}
};
}
int main() {
Point p1(3,2);
Point p2(3,2);
std::unordered_set<Point> someSet;
someSet.insert(p1);
std::cout << "set size:" << someSet.size() << std::endl;
// set size: 1
someSet.insert(p2);
std::cout << "set size:" << someSet.size() << std::endl;
// set size: 1 (still!)
return 0;
}
Upvotes: 2
Reputation: 352
If only presence is relevant: std::set
.
If you need also occurrence count, you could use std::map<Point, int>
, where the int
is the count. With some special handling to remove elements when counts gets to zero.
You could replace std::map
with std::unordered_map
for performance reasons. But ALWAYS test both before deciding.
Upvotes: 0