I'm having problems and I'm not sure I understand the STL documentation. Let's say I have this:
#include <set>
...
struct foo
{
int bar;
};
struct comp
{
inline bool operator()(const foo& left,const foo& right)
{
return left.bar < right.bar;
}
};
int main()
{
std::set<foo,comp> fooset; // Uses comparison struct/class object comp to sort the container
...
return 0;
}
How do I insert struct foos into the set using my own comparator struct?
You can use the set::insert method, there is nothing more to do. For example,
foo f1, f2;
f1.bar = 10;
f2.bar = 20;
fooset.insert(f1);
fooset.insert(f2);
Related
say I have
class newVector: public std::vector<T> {
public:
bool operator< (const newVector& v) {
//..
}
};
And
a std::set<newVector>;
I can't manage to use a.find(...) properly, I am not sure what to put into the (...) in order to use newVector::operator<. When I just put a.find(element) it uses std::less. Should I change std::less somehow?
Ignoring for the time being that deriving from std::vector is a bad idea, I can think of the following ways to address the issue:
Define operator< for objects of newVector.
class newVector: public std::vector<T> {
public:
bool operator< (const newVector& v) const {
//..
}
and
std::set<newVector> a;
a.find(...);
Define a functor that has appropriate operator() function and use it to create the std::set.
template <typename T>
struct NewVectorLess
{
bool operator()(newVector<T> const& lhs, newVector<T> const& rhs)
{
// ...
}
};
and
std::set<newVector<int>, NewVectorLess<int>> a;
a.find(...);
You don't need to overload the vector, or to change std::less, but to define separately your own std::less compatible function object.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
struct OppositeVectorComp
{
template< class T, class Alloc >
bool operator()( const std::vector<T,Alloc>& lhs,const std::vector<T,Alloc>& rhs )
{
return !(lhs < rhs);
}
};
int main() {
std::vector<int> a , b;
std::set<std::vector<int>> defaultset;
std::set<std::vector<int>, OppositeVectorComp> myset;
a.push_back(1);
b.push_back(2);
myset.insert(a);
myset.insert(b);
defaultset.insert(a);
defaultset.insert(b);
std::cout << (*myset.begin())[0] << std::endl; // output 2
std::cout << (*defaultset.begin())[0] << std::endl; // output 1
return 0;
}
Here OppositeVectorComp define a new order on vectors where
OppositeVectorComp(a,b) true iff a <b is false
By using the type std::set<std::vector<int>, OppositeVectorComp> we define a set which use the custom std::less.
In java you could have something like:
Map<Foo, List<Bar>> things;
for(Foo foo : things.getKeySet()){
List<bar> = things.get(foo);
}
Is there an equivalent for c++, maybe in std::map? Thanks for any help.
See std::map and std::vector (ArrayList) and maybe std::unordered_map (HashMap) and std::list (LinkedList)
For example:
#include <map>
#include <vector>
struct Foo {};
struct Bar {};
int main()
{
std::map<Foo, std::vector<Bar>> things;
for(auto& thing: things) {
const Foo& foo = thing.first; // key
std::vector<Bar>& bars = thing.second; // value
// use foo & bars here
}
}
Note: A std::map requires that a comparison operator be defined for user defined types like Foo:
struct Foo
{
int i = 0;
Foo(int i): i(i) {}
// need a comparison operator for ordered containers
bool operator<(const Foo& foo) const { return i < foo.i; }
};
I have a struct with two members an int and a string.
struct A
{
int a;
string b;
}
vector<A> pl;
pl = getPL(); //getPL returns a vector<A>
for ( auto: pl )
{
vector<A>tr;
tr = getTR() //getTR returns vector<A>
for(auto: tr)
{
//store tr somewhere..
}
}
I want to create a map<A, vector<A>> so that each pl object can hold one vector. How to do that? Is there any other way or data structure in c++ that can achieve what I am trying to do.
Thanks,
std::maps keep their data sorted by key. But to do this, they need some way to compare two key objects and determine which one should come first. By default, this comparison is done using the < operator, but that operation is not defined for A, which (probably) explains the compile error you are (probably) seeing.
So; to use A as the key in a std::map, you will either need to define operator < for it or provide the map with a custom key_compare functor. The code below demonstrates both approaches:
#include <map>
#include <string>
#include <vector>
struct A
{
int a;
std::string b;
};
bool operator <(const A& l, const A& r)
{
if(l.a != r.a)
return l.a < r.a;
return l.b < r.b;
}
struct B
{
int a;
std::string b;
};
struct CompareBs
{
bool operator()(const B& l, const B& r) const
{
if(l.a != r.a)
return l.a < r.a;
return l.b < r.b;
}
};
int main()
{
std::map<A, std::vector<A>> aMap;
std::map<B, std::vector<B>, CompareBs> bMap;
return 0;
}
I'm trying to hold a polymorphic type as a key in a map.
I came up with the following two structures:
Note that Game is an abstract class and the data structure I use is :
std::unordered_map<gamePtr,int> _allGames;
while gamePtr is a typedef for:
unique_ptr<Game>
template<>
struct std::hash<std::unique_ptr<Game>> {
size_t operator()(std::unique_ptr<Game> game) const {
return (std::hash<string>()(std::to_string(game->firstTeamFinalScore()) + game->firstTeam() + game->secondTeam()));
}
};
struct cmp_games {
bool operator() (std::unique_ptr<Game> game1, std::unique_ptr<Game> game2) const {
return *game1 == *game2;
}
};
The cmp_games comparator seems to work fine but the std::hash does not because it tries to copy a unique_ptr (Which is ofc impossible) and I've no idea how to get over it.
Would love to hear some suggestions (If that is even possible).
EDIT: The comparator also doesn't seem to work properly. how do I make this map work correctly with unique_ptr as a key?
EDIT2:
Came up with:
template<>
struct std::hash<std::unique_ptr<Game>> {
size_t operator()(const std::unique_ptr<Game>& game) const {
return (std::hash<string>()(std::to_string(game->firstTeamFinalScore()) + game->firstTeam() + game->secondTeam()));
}
};
template<>
struct std::equal_to<std::unique_ptr<Game>> {
bool operator() (const std::unique_ptr<Game>& game1,const std::unique_ptr<Game>& game2) const {
return *game1 == *game2;
}
};
Should they be enough?
The standard provides a specilization so that std::hash<unique_ptr<T>> is the same as std::hash<T*>. So provide a specialization for std::hash<Game *>. For example:
#include <iostream>
#include <memory>
#include <unordered_map>
#include <cstdlib>
struct foo
{
foo(unsigned i) : i(i) {}
unsigned i;
};
namespace std {
template<>
struct hash<foo *>
{
size_t operator()(foo const *f) const
{
std::cout << "Hashing foo: " << f->i << '\n';
return f->i;;
}
};
}
int main()
{
std::unordered_map<std::unique_ptr<foo>, int> m;
m.insert(std::make_pair(std::unique_ptr<foo>(new foo(10)), 100));
m.insert(std::make_pair(std::unique_ptr<foo>(new foo(20)), 200));
}
Live demo
Another option is to change your existing std::hash specialization so that it takes the unique_ptr by reference.
size_t operator()(std::unique_ptr<Game> const& game) const
// ^^^^^^ no more copying
EDIT: std::unique_ptr provides comparison operators that compare the managed pointers. If you want the unordered_map to test the Game objects themselves for equality, provide an operator== overload instead of specializing std::equal_to
inline bool operator==(const std::unique_ptr<Game>& game1,
const std::unique_ptr<Game>& game2)
{
return *game1 == *game2;
}
This, in turn, requires that you've provided an equality operator for Game (or you could just add the logic to the function above).
inline bool operator==(Game const& game1, Game const& game2)
{
return // however you want to compare these
}
Pass the game by const reference into std::hash::operator():
template<>
struct std::hash<std::unique_ptr<Game>> {
size_t operator()(const std::unique_ptr<Game>& game) const;
}
The same applies to cmp_games::operator().
Like qsort(), it seems that C++ std::sort() does not allow to pass user data to the sort function.
For example:
An array of structure like struct A { int version; int index; } array[100] has to be sorted in order, but using this array struct B { int value; } key[100] as the sort key. struct A::index indexes array key.
Here's a non-working sort function. It needs to have a pointer to the key array somehow:
bool comp(struct A *a1, struct A *a2) {
return key[a1->index].value < key[a2->index].value;
}
How to achieve that using C++? How to pass non-global user data like key to a sort function?
I tried to pass an object instance as the std::sort comp, but it seems only qsort()-like functions are allowed.
(In GNU C, a nested compare function could be used to use scoped variables, but GNU C++ does not offer nested functions).
Functors don't have to be functions; they can be objects.
struct Comparator {
Comparator(int* key) : key(key) {};
bool operator()(struct A *a1, struct A *a2) {
return key[a1->index].value < key[a2->index].value;
}
int* key;
};
/* ... */
std::sort(container.begin(), container.end(), Comparator(<pointer-to-array>));
You can tell sort exactly how to sort through the use of a comparison functor.
Working example:
struct Foo
{
int a_;
std::string b_;
};
Foo make_foo(int a, std::string b)
{
Foo ret;
ret.a_ = a;
ret.b_ = b;
return ret;
}
struct ByName : public std::binary_function<Foo, Foo, bool>
{
bool operator()(const Foo& lhs, const Foo& rhs) const
{
return lhs.b_ < rhs.b_;
}
};
template<class Stream> Stream& operator<<(Stream& os, const Foo& foo)
{
os << "[" << foo.a_ << "] = '" << foo.b_ << "'";
return os;
}
int main()
{
vector<Foo> foos;
foos.push_back(make_foo(1,"one"));
foos.push_back(make_foo(2,"two"));
foos.push_back(make_foo(3,"three"));
sort(foos.begin(), foos.end(), ByName());
copy(foos.begin(), foos.end(), ostream_iterator<Foo>(cout, "\n"));
}
Output:
[1] = 'one'
[3] = 'three'
[2] = 'two'