std::set different comparer for inserting and ordering - c++

I need a structure in which I can insert elements, have no duplicate, use a custom comparer and have the smallest element first. I tried using std::priority_queue, but the problem is that I get a lot of duplicates and I run out of space. So I thought about using std::set : std::set< std::pair<Coordinates, int>, Compare> positions; where
Coordinates
{
public:
Coordinates(int x = 0, int y = 0, char tool = 'T') : x(x), y(y), tool(tool) {}
public:
int x, y;
char tool;
};
class Compare
{
public:
bool operator() (const std::pair<Coordinates, int>& c1, const std::pair<Coordinates, int>& c2) const
{
return c1.second < c2.second;
}
};
I want the elements to be sorted based on the second element of pair, which this implementation is doing, but the problem is that it is using the same comparer when inserting new pairs and I get duplicates. My question is: Is it possible to make the std::set to not allow duplicates also to order the elements based on the second element of pair?
Edit: Eliminated some code that was not necessary and changed in Compare > with <

Using your Comparer the set will contain only unique values of the int, since Coordinates isn't participating in the comparison at all.
std::set uses operator < for sorting as well as equality; equality is determined as !(a<b || b<a). Therefore operator < should take into account every attribute which makes the element unique.
You can specialize std::less for your type like this:
namespace std {
template<>
struct less<pair<Coordinates, int>> {
bool operator()(const pair<Coordinates, int>& a, const pair<Coordinates, int>& b) const {
return tie(a.second, a.first.x, a.first.y) < tie(b.second, b.first.x, b.first.y);
}
};
}
Then std::set< std::pair<Coordinates, int>> positions; should work.

The issue here is that since you only look at second in you comparator, you can only store pairs that have unique values for second. This is because the set only uses the comparator to compare the elements. It doesn't use your operator == to check for equality but instead does cmp(a, b) == cmp(b, a)1 to test if the values are equal.
If you wan to sort by second, but allow other points with the same second but different other values then you need to add those values into you comparator. The easiest way to do that is to use std::tie to build a couple of tuples and use the tuples operator < which "does the right thing". That would look like
class Compare
{
public:
bool operator() (const std::pair<Coordinates, int>& c1, const std::pair<Coordinates, int>& c2) const
{
return std::tie(c1.second, c1.first.x, c1.first.y) < std::tie(c2.second, c2.first.x, c2.first.y);
}
};
1: If a is not less then b, and b is not less than a then a and b must be equal

As stated, the issue was that you only looked at the second member of the pair, so the set didn't care if the Coordinates were different. You simply needed to include the Coordinates in your comparison.
Unlike the other answers, this one utilizes a lambda for the comparison. I prefer it over std::tie and mucking around with std overrides. It also saves you the trouble of writing up a functor yourself like you did with your Compare class.
#include <iostream>
#include <set>
class Coordinates {
public:
Coordinates(int x = 0, int y = 0, char tool = 'T') : x(x), y(y), tool(tool) {}
int x, y;
char tool;
};
int main() {
using CoordPair = std::pair<Coordinates, int>;
auto compare = [](const CoordPair& a, const CoordPair& b) {
if (a.second != b.second)
return a.second < b.second;
// Replace this with some method of comparing Coordinates
return a.first.x != b.first.x || a.first.y != b.first.y;
};
std::set<std::pair<Coordinates, int>, decltype(compare)> list(compare);
list.emplace(Coordinates(1, 1), 2);
list.emplace(Coordinates(2, 0), 2);
list.emplace(Coordinates(1, 1), 3);
list.emplace(Coordinates(1, 1), 2); // Shouldn't show up
for (auto i : list)
std::cout << '(' << i.first.x << ", " << i.first.y << ", " << i.first.tool
<< ')' << ", " << i.second << '\n';
}
Your C++ version wasn't specified, this needs at least C++11.

Related

Getting error while sorting a map by value [duplicate]

I was trying to make a map sort by value using a custom comparator but I couldn't figure out why I kept getting the error of "no matching call to compareByVal"
Here's what I had in my main.cpp:
#include <map>
#include <iostream>
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
int main() {
std::map<int,int,compareByVal> hash;
hash[1] = 5;
hash[2] = 2;
hash[3] = 10;
std::cout << hash.begin()->first << std::endl;
}
The first, simple problem is
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
should be
struct compareByVal {
bool operator()(const std::pair<int,int> & a, const std::pair<int,int> & b) const {
return a.second < b.second;
}
};
The second, serious problem is the signature of the compare is wrong. It should be
struct compareByVal {
bool operator()(const int leftKey, const int rightKey) const;
}
You can't access the value in the compare function. There is no (simple) way to sort a map by value.
Simply put, you cannot. Not sure which compiler you're using, but clang and gcc both give useful messages. with context.
clang:
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
gcc:
if (__i == end() || key_comp()(__k, (*__i).first))
You can see that clang and gcc are both calling the compare method with only they key, and not a value. This is simply how maps work.
If you want to sort by value, you would have to create your own custom map, or, more realistically, use the value as the key instead. Creating your own map to achieve this would be more difficult than you'd think, since it would have to sort after any value is modified.
If you want to sort a std::map by its value, then you are using the wrong container. std::map is sorted by the keys by definition.
You can wrap key and value:
struct foo {
int key;
int value;
};
and then use a std::set<foo> that uses a comparator that only compares foo::value.
Well, first, the reason you're getting the error: "no matching call to compareByVal" is because map's comparator works only with the keys. So the comparator should like:
struct compareByVal {
template <typename T>
bool operator()(const T& a, const T& b) const
return a < b;
}
Coming on to what you want to achieve, I see two ways of doing so:
Copy all the elements of the map to a std::vector and sort that:
std::vector<std::pair<int,int> > v(hash.begin(), hash.end());
std::sort(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
Copy all the elements of the map to another map with keys as values and values as keys. If values of your map are not unique, you can use a std::multimap instead.
This may be an X-Y issue.
If you need to sort by both key and value, then a single std::map may not be the most efficient choice.
In database theory, all the data would be placed into a single table. An index table would be created describing the access or sorting method. Data that needs to be sorted in more than one method would have multiple index tables.
In C++, the core table would be a std::vector. The indices would be std::map<key1, vector_index>, std::map<key2, vector_index>, where vector_index is the index of the item in the core table.
Example:
struct Record
{
int age;
std::string name;
};
// Core table
std::vector<Record> database;
// Index by age
std::map<int, unsigned int> age_index_table;
// Index by name
std::map<std::string, unsigned int> name_index_table;
// Fetching by age:
unsigned int database_index = age_index_table[42];
Record r = database[database_index];
// Fetching by name:
unsigned int database_index = name_index_table["Harry Potter"];
Record r = database[database_index];
You can learn more by searching the internet for "database index tables c++".
If it looks like a database and smells like a database ...

cpp unordered_set just use comparator not hash

#include <unordered_set>
#include <iostream>
class edge{
public:
float a1;
float a2;
};
struct comp{
bool operator()(const edge& e1, const edge& e2) const {
return true;
return (
(e1.a1==e2.a1 && e1.a2==e2.a2) ||
(e1.a1==e2.a2 && e1.a2==e2.a1)
);
};
};
struct hash{
size_t operator()(const edge& e1) const {
// return std::hash<float>()(e1.a1+e1.a2);
return std::hash<float>()(e1.a1+e1.a2*2);
};
};
int main() {
std::unordered_set<edge,hash,comp> s1;
s1.insert(edge{1.1,2.2});
s1.insert(edge{2.2,1.1});
for( auto& it : s1 ) {
std::cout << it.a1 << " " << it.a2 << "\n";
}
std::cout << "s1.size " << s1.size() << "\n";
}
I realize that if different element has same hash value, then they are considered equal, but I just want this unordered_set use comparator that I define, just ignore hash?
How to achieve that?
I know i can use set, but using set need to consider order, if a < b is true and b < a is also true, then this element will not be inserted successfully, Sometimes, It is hard to provide order.
If anyone can help, much appreciated
edited:
My intention is to let two edges called e1,e2, they are same if (e1.a1==e2.a1&&e1.a2==e2.a2)or(e1.a1==e2.a2 && e1.a2==e2.a1) as I provided
in struct comp.
but when i test. it seems hash function can change the comparison too. Someone says the way I define hash and comparator result in undefined behaviour.
Is that true? why?
if true, how to solve this? I just want comparator decide which one is satisfied to be inserted in unordered_set without duplicate. And really do not care about hash.
BTW, thanks for some many people replying
If you want to handle edge.a1 and edge.a2 interchangeably, you have to implement a hashing function that returns the same value even when they are swapped. I advise against using addition, because addition may not be commutative for floats, but you could sort them by size and combine the hashes afterwards:
struct hash {
size_t operator()(const edge& e1) const {
auto h1 = std::hash<float>{}(std::min(e1.a1, e1.a2));
auto h2 = std::hash<float>{}(std::max(e1.a1, e1.a2));
return h1 ^ (h2 << 1)
};
};
This only makes sense for pretty large sets of floats, because otherwise the hashing overhead probably exceeds the benefit of using a hashed data structure in the first place.
Old answer for reference:
Objects with the same hash are not considered equal in
unordered_set. They are just stored in the same bucket. There is a
KeyEqual template parameter for the comparison, which by
default uses the operator== of your Key. So your main problem is,
that comp should implement e1 == e2 and not e1 < e2 (and should
probably be called equal).
The hash is just used to speed up the search, insertion, and removal
of elements.
On another note, you may want to use the hashes of the member
variables instead of the values themselves to compute the hash of
edge:
struct hash {
size_t operator()(const edge& e1) const {
auto h1 = std::hash<float>{}(e1.a1);
auto h2 = std::hash<float>{}(e1.a2);
return h1 ^ (h2 << 1)
};
};
This way, you won't get the same hash for two edges with swapped
coordinates. This way of combining hashes is suggested here (but
is not a good way to combine more than two).
You don't have to use the members of edge to provide the hash. It is only required that equal values have equal hashes. A "always valid" hash is
struct hash{
size_t operator()(const edge& e1) const {
return 0;
};
};
But it seems your original attempt is better
struct hash{
size_t operator()(const edge& e1) const {
return std::hash<float>{}(e1.a1 + e1.a2); // + is commutative
};
};

Topological sorting using std::sort

Note: While writing this question, I think I already found the answer. Feel free to ammend or append it with a better version. I thought it might be nice to document my problem. edit I was wrong, my aswer was not correct.
Considering a list of integer pairs: I'd like to topologically sort them based on a partial ordering. This is similar to Is partial-order, in contrast to total-order, enough to build a heap? , but I'd like to use std::sort instead of std::priority_queue.
To do so I wrote this piece of code:
#include <iostream>
#include <vector>
#include <algorithm>
struct pair {
int a, b;
pair(int a, int b) : a(a), b(b) {}
std::ostream &print(std::ostream &out) const {
return (out << "(" << a << ", " << b << ")");
}
};
std::ostream &operator<<(std::ostream &out, const pair &p) { return p.print(out); }
struct topological_pair_comparator {
bool operator()(const pair &p, const pair &q) const { return p.a<q.a && p.b<q.b; }
} tpc;
std::vector<pair> pairs = {
pair(1,1),
pair(1,2),
pair(2,1),
pair(3,1),
pair(1,3),
pair(5,5),
pair(2,2),
pair(4,0)
};
int main() {
std::sort(pairs.begin(), pairs.end(), tpc);
for(const pair &p : pairs) std::cout << p << " ";
std::cout << std::endl;
return 0;
}
Source: http://ideone.com/CxOVO0
Resulting in:
(1, 1) (1, 2) (2, 1) (3, 1) (1, 3) (2, 2) (4, 0) (5, 5)
Which is pretty much topologially sorted (proof by example ;).
However, the partial ordering creates that !((1,2) < (2,1)) and !((1,2) > (2,1)) according to the tpc, and hence one may conclude (1,2) == (2,1). However, paragraph 25.4.3 of the c++ standard (January 2012 working draft) states:
For all algorithms that take Compare, there is a version that uses operator< instead. That is, comp(*i,
*j) != false defaults to *i < *j != false. For algorithms other than those described in 25.4.3 to work
correctly, comp has to induce a strict weak ordering on the values.
Edited: According to ecatmur 's valid answer:
A partial ordering is not necessarily a strict weak ordering; it breaks the transitivity of incomparibility. So I'd like to drop my reasoning that a partial ordering is always a strict weak ordering and the associated questions, and add the question: am I doomed to write my own topological sorting algorithm or use the boost implementation which requires me to build the graph?
Solution: A smart suggestion of ecatmur:
struct topological_pair_comparator {
bool operator()(const pair &p, const pair &q) const { return (p.a + p.b) < (q.a + q.b); }
} tpc;
Source: http://ideone.com/uoOXNC
Please note, the SO about heaps does not explicitely mention that std::sort sorts topologically; except for one comment, which is not backed up by argumentation.
Consider the values
pair
x{0, 1},
y{2, 0},
z{1, 2};
Here,
!tpc(x, y) && !tpc(y, x);
!tpc(y, z) && !tpc(z, y);
However,
tpc(x, z);
Thus your comparator does not impose a strict weak ordering, and behavior is undefined if you use it with std::sort or in any other role where a strict weak ordering is required.
A comparator that is strict weak and is a refinement of your comparator would be:
struct refined_comparator {
bool operator()(const pair &p, const pair &q) const { return p.a + p.b < q.a + q.b; }
} rc;

Is there a more efficient implementation for a bidirectional map?

I created a simple bidirectional map class that works by internally storing two std::map instances, with opposite key/value types, and providing a user-friendly interface:
template<class T1, class T2> class Bimap
{
std::map<T1, T2> map1;
std::map<T2, T1> map2;
// ...
};
Is there a more efficient method of implementing a bidirectional map that doesn't require twice the memory?
How is a bimap usually implemented?
EDIT:
Should bimap element be mutable or immutable? (Changing one element in map1 should change the key in map2, but keys are const and that's impossible - what's the solution?)
Ownership of elements is also another problem: when a user inserts a key-value pair in the bimap, the bimap should make a copy of that key-value pair and store it, then the internal second map (with inverted key/value) should not copy but point to the original pair. How can this be achieved?
EDIT 2:
I've posted a possible implementation I made on Code Review.
There is a certain problem with double-storing your data in all simple implementations of a bimap. If you can break it down to a bimap of pointers from outside, then you can readily ignore this and simply keep both maps of the form std::map<A*,B*> like Arkaitz Jimenez already suggested (though contrary to his answer you have to care about the storage from outside to avoid a A->A* lookup). But if you have the pointers anyway, why not simply store a std::pair<A,B> at the point where you would otherwise store A and B separately?
It would be nice to have std::map<A,B*> instead of std::map<A*,B*> as this would allow for example the lookup of an element associated to an string by a newly created string with the same content instead of the pointer to the original string that created the pair. But it is customary to store a full copy of the key with every entry and only rely on the hash to find the right bucket. This way the returned item will be the correct one even in the case of a hash-collision...
If you want to have it quick and dirty though, there is this
hackish solution:
Create two maps std::map<size_t, A> mapA and std::map<size_t, B> mapB. Upon insertion hash both elements that are to be inserted to get the keys to the respective maps.
void insert(const A &a, const B &b) {
size_t hashA = std::hash<A>(a);
size_t hashB = std::hash<B>(b);
mapA.insert({hashB, a});
mapB.insert({hashA, b});
}
Lookup is implemented analogously.
Using a multimap instead of a map and verifying every element you get with a lookup in the respectively other map (get candidate b from mapA, hash b and look in mapB if it matches the wanted key, iterate to the next candidate b otherwise) this is a valid implementation - but still hackish in my opinion...
You can get a much nicer solution by using the copies of the elements that are used to compare the entries (see above) as only storage. It is a bit harder to get your head around that though. To elaborate:
a nicer solution:
Create two sets of pairs as std::set<pair<A, B*>> and std::set<pair<B, A*>> and overload the operator< and operator== to only take the first element of the pairs into account (or provide an corresponding comparion class). It is necessary to create sets of pairs instead of maps (which internally look similarly) because we need a guarantee that A and B will be at constant positions in memory. Upon insertion of an pair<A, B> we split it into two elements that fit into the above sets.
std::set<pair<B, A*>> mapA;
std::set<pair<A, B*>> mapB;
void insert(const A &a, const B &b) {
auto aitr = mapA.insert({b, nullptr}).first; // creates first pair
B *bp = &(aitr->first); // get pointer of our stored copy of b
auto bitr = mapB.insert({a, bp}).first;
// insert second pair {a, pointer_to_b}
A *ap = &(bitr->first); // update pointer in mapA to point to a
aitr->second = ap;
}
Lookup can now simply be done by a simple std::set lookup and a pointer dereference.
This nicer solution is similar to the solution that boost uses - even though they use some annonymized pointers as second elements of the pairs and thus have to use reinterpret_casts.
Note that the .second part of the pairs need to be mutable (so I'm not sure std::pair can be used), or you have to add another layer of abstraction (std::set<pair<B, A**>> mapA) even for this simple insertion. In both solutions you need temporary elements to return non-const references to elements.
It would be more efficient to store all elements in a vector and have 2 maps of <T1*,T2*> and <T2*,T1*> that way you would not have everything copied twice.
The way I see it you are trying to store 2 things, elements themselves and the relationship between them, if you are aiming to scalar types you could leave it as is 2 maps, but if you aim to treat complex types it makes more sense to separate the storage from the relationships, and handle relationships outside the storage.
Boost Bimap makes use of Boost Mutant Idiom.
From the linked wikipedia page:
Boost mutant idiom makes use of reinterpret_cast and depends heavily on assumption that the memory layouts of two different structures with identical data members (types and order) are interchangeable. Although the C++ standard does not guarantee this property, virtually all the compilers satisfy it.
template <class Pair>
struct Reverse
{
typedef typename Pair::first_type second_type;
typedef typename Pair::second_type first_type;
second_type second;
first_type first;
};
template <class Pair>
Reverse<Pair> & mutate(Pair & p)
{
return reinterpret_cast<Reverse<Pair> &>(p);
}
int main(void)
{
std::pair<double, int> p(1.34, 5);
std::cout << "p.first = " << p.first << ", p.second = " << p.second << std::endl;
std::cout << "mutate(p).first = " << mutate(p).first << ", mutate(p).second = " << mutate(p).second << std::endl;
}
The implementation in boost sources is of course fairly hairier.
If you create a set of pairs to your types std::set<std::pair<X,Y>> you pretty much have your functionallity implemented and rules about mutabillity and constness preset (OK maybe the settings aren't what you want but tweaks can be made). So here is the code :
#ifndef MYBIMAP_HPP
#define MYBIMAP_HPP
#include <set>
#include <utility>
#include <algorithm>
using std::make_pair;
template<typename X, typename Y, typename Xless = std::less<X>,
typename Yless = std::less<Y>>
class bimap
{
typedef std::pair<X, Y> key_type;
typedef std::pair<X, Y> value_type;
typedef typename std::set<key_type>::iterator iterator;
typedef typename std::set<key_type>::const_iterator const_iterator;
struct Xcomp
{
bool operator()(X const &x1, X const &x2)
{
return !Xless()(x1, x2) && !Xless()(x2, x1);
}
};
struct Ycomp
{
bool operator()(Y const &y1, Y const &y2)
{
return !Yless()(y1, y2) && !Yless()(y2, y1);
}
};
struct Fless
{ // prevents lexicographical comparison for std::pair, so that
// every .first value is unique as if it was in its own map
bool operator()(key_type const &lhs, key_type const &rhs)
{
return Xless()(lhs.first, rhs.first);
}
};
/// key and value type are interchangeable
std::set<std::pair<X, Y>, Fless> _data;
public:
std::pair<iterator, bool> insert(X const &x, Y const &y)
{
auto it = find_right(y);
if (it == end()) { // every .second value is unique
return _data.insert(make_pair(x, y));
}
return make_pair(it, false);
}
iterator find_left(X const &val)
{
return _data.find(make_pair(val,Y()));
}
iterator find_right(Y const &val)
{
return std::find_if(_data.begin(), _data.end(),
[&val](key_type const &kt)
{
return Ycomp()(kt.second, val);
});
}
iterator end() { return _data.end(); }
iterator begin() { return _data.begin(); }
};
#endif
Example usage
template<typename X, typename Y, typename In>
void PrintBimapInsertion(X const &x, Y const &y, In const &in)
{
if (in.second) {
std::cout << "Inserted element ("
<< in.first->first << ", " << in.first->second << ")\n";
}
else {
std::cout << "Could not insert (" << x << ", " << y
<< ") because (" << in.first->first << ", "
<< in.first->second << ") already exists\n";
}
}
int _tmain(int argc, _TCHAR* argv[])
{
bimap<std::string, int> mb;
PrintBimapInsertion("A", 1, mb.insert("A", 1) );
PrintBimapInsertion("A", 2, mb.insert("A", 2) );
PrintBimapInsertion("b", 2, mb.insert("b", 2));
PrintBimapInsertion("z", 2, mb.insert("z", 2));
auto it1 = mb.find_left("A");
if (it1 != mb.end()) {
std::cout << std::endl << it1->first << ", "
<< it1->second << std::endl;
}
auto it2 = mb.find_right(2);
if (it2 != mb.end()) {
std::cout << std::endl << it2->first << ", "
<< it2->second << std::endl;
}
return 0;
}
NOTE: All this is a rough code sketching of what a full implementation would be and even after polishing and extending the code I'm not implying that this would be an alternative to boost::bimap but merely a homemade way of having an associative container searchable by both the value and the key.
Live example
One possible implementation that uses an intermediate data structure and an indirection is:
int sz; // total elements in the bimap
std::map<A, int> mapA;
std::map<B, int> mapB;
typedef typename std::map<A, int>::iterator iterA;
typedef typename std::map<B, int>::iterator iterB;
std::vector<pair<iterA, iterB>> register;
// All the operations on bimap are indirected through it.
Insertion
Suppose you have to insert (X, Y) where X, Y are instances of A and B respectively, then:
Insert (X, sz) in mapA --- O(lg sz)
Insert (Y, sz) in mapB --- O(lg sz)
Then push_back (IterX, IterY) in register --- O(1). Here IterX and IterY are iterators to the corresponding element in mapA and mapB and are obtained from (1) and (2) respectively.
Lookup
Lookup for the image of an element, X, of type A:
Get the int mapped to X in mapA. --- O(lg n)
Use the int to index into register and get corresponding IterY. --- O(1)
Once you have IterY, you can get Y through IterY->first. --- O(1)
So both the operations are implemented in O(lg n).
Space: All the copies of the objects of A and B are required to be stored only once. There is, however, a lot of bookkeeping stuff. But when you have large objects, that would also be not much significant.
Note: This implementation relies on the fact that the iterators of a map are never invalidated. Hence, the contents of register are always valid.
A more elaborate version of this implementation can be found here
How about this?
Here, we avoid double storage of one type (T1). The other type (T2) is still double stored.
// Assume T1 is relatively heavier (e.g. string) than T2 (e.g. int family).
// If not, client should instantiate this the other way.
template <typename T1, typename T2>
class UnorderedBimap {
typedef std::unordered_map<T1, T2> Map1;
Map1 map1_;
std::unordered_map<T2, Map1::iterator> map2_;
};

Reversing the content of a set in C++

I want to reverse the contents of a std::set(Not just iterating theough it in reverse, but reversing the contents iteslf). I found that std::set takes the compare as a function object for its constructor. Hence I came up with the following code to do the same:
#include <set>
using namespace std;
struct Comparator
{
Comparator(bool g) : m_g(g){}
bool operator()(int i1, int i2) const
{
if(m_g)
{
return i1>i2;
}
return i1<i2;
}
bool m_g;
};
int main(int argc, char** argv)
{
Comparator l(false);
set<int,Comparator> a(l);
a.insert(1);
a.insert(2);
a.insert(3);
Comparator g(true);
set<int,Comparator> b(g);
copy(a.begin(), a.end(), inserter(b, b.begin()));
a = b;
return 0;
}
This seems to work in VC9. But is this code correct? My doubt arises due to the fact my Comparator has state associated with it. Are comparators are allowed to have states?
Yes, that's legal. Consider that if the comparator was not allowed to have state, there would be no point in allowing you to pass a comparator as a constructor parameter. :)
As long as the comparator provides a strict weak ordering, it's fine (which, among other things, means that it has to be consistent. You can't change the state of it halfway through, so that it orders elements differently)
It's fine, but it's needlessly complex.
You can just use std::less (the default value for that template parameter!) or std::greater from the standard library. They are provided by <functional>.
A more generic solution. boost::assign and c++11 just for convenience (and the funny auto reverse)
# include <iostream>
# include <set>
# include <boost/assign.hpp>
using namespace boost::assign;
template <typename CL , typename Pred>
struct revPred {
revPred (Pred pred) : pred_(pred) {}
bool operator()(const CL & a, const CL& b)
{
return pred_(b,a);
}
Pred pred_;
};
template <typename CL , typename Pred, typename alloc>
inline
std::set<CL,revPred<CL,Pred>,alloc> reverseSet(const std::set<CL,Pred,alloc> & set) {
std::set<CL,revPred<CL,Pred>,alloc> res(revPred<CL,Pred>(set.key_comp()));
std::copy(set.begin(), set.end(), std::inserter(res, res.begin()));
return res;
}
int main()
{
std::set<int> s; s += 0 , 1 , 2 , 3;
std::for_each(s.begin(), s.end(), [](int x) { std::cout << x << " "; });
std::cout << std::endl;
auto reverse = reverseSet(s);
std::for_each(reverse.begin(), reverse.end(), [](int x) { std::cout << x << " "; });
std::cout << std::endl;
return 0;
}
There is nothing wrong with your code.
And there is nothing wrong with comparators having state.
This is ok since your comparison doesn't change dynamically and it does provide strict weak ordering.
However, if you're doing this so that the type of the set is the same even when the order changes, I might suggest an alternate idea. Instead of this comparison, you use two different set types with std::less and std::greater and use an iterator interface like the standard library does, rather than a container interface that depends on all the template parameters.
And finally as noted in the answer from #parapura rajkumar you should use the iterator pair constructor rather than std::copy:
// Assuming my other comments don't apply, modify as needed if they do:
Comparator g(true);
set<int, Comparator> b(a.rbegin(), a.rend(), g);
You don't need to provide a function object which maintains state. Just use a normal function and it should do the job. Pls. don't mind for the use of lambda. Used as a short cut to do the printing.
typedef bool (*Cmp)(int x, int y);
bool Compare(int x, int y)
{
return x < y;
}
bool CompareR(int x , int y)
{
return !Compare(x, y);
}
void SetReverse()
{
std::set<int, Cmp> s1(Compare);
s1.insert(1);
s1.insert(3);
s1.insert(2);
s1.insert(4);
std::for_each(s1.begin(), s1.end(), [](int x) { std::cout << x << "\n"; });
std::set<int, Cmp> s2(CompareR);
s2.insert(s1.begin(), s1.end());
std::for_each(s2.begin(), s2.end(), [](int x) { std::cout << x << "\n"; });
s1 = s2;
}
If you have a large enough set you have already paid the O(NlogN) penalty to make a balanced binary tree. A blind insert into the destination set, you will have to pay the penalty again.
Consider using one of these insert overload.
void insert ( InputIterator first, InputIterator last );
iterator insert ( iterator position, const value_type& x );
The range insert has linear complexity if [ first , last ) are sorted already.