I have a vector of pointers to structs, and want to check for existing items and sort by the value of a struct member. However, it appears that the check for existing items (I'm using QVector::indexOf()) is not working, and that std::sort is applying its sorting to some pointer value rather than the member value. I don't understand why the struct operator overloads aren't getting called (or called properly).
In header file:
struct PlotColumn {
quint32 octagonLength;
QVector<Qt::GlobalColor> columnVector;
bool operator< (const PlotColumn& pc) const
{
return (this->octagonLength < pc.octagonLength);
}
bool operator==(PlotColumn const& pc)
{
return this->octagonLength == pc.octagonLength;
}
bool operator> (const PlotColumn& pc) const
{
return (this->octagonLength > pc.octagonLength);
}
};
QVector<PlotColumn*> *plotMap;
In source file:
PlotColumn *pcNew = new PlotColumn();
pcNew->octagonLength = octagonLength;
// check if octagonLength has arrived before:
int plotXAxis = plotMap->indexOf(pcNew);
if (plotXAxis == -1) { // unknown, so insert:
... (some housekeeping)
plotMap->append(pcNew);
std::sort(plotMap->begin(), plotMap->end());
....
(some more housekeeping)
}
Using an external (not in the struct) is possible for sort, but not for indexOf (afaik).
Any thoughts?
PS: yes I know there are tons of already answered questions about sorting vectors of pointers to structs and I've tried to apply the pattern in those solutions but it still doesn't work.
You need to provide a comparater to std::sort() that works with pointers.
bool PlotColumnPointerComparator(const PlotColumn *a, const PlotColumn *b)
{
return (*a) < (*b); // this will call your PlotColumn:: operator<()
}
And change your std::sort() statement to
std::sort(plotMap->begin(), plotMap->end(), PlotColumnPointerComparator);
In C++11, you could do the above with a lambda function, but that's just a syntactic convenience.
Compilers are not omnipotent mindreaders. If you tell it to sort a set of pointers, it will do pointer comparison. If you want the comparison to dereference the pointers and compare the pointed-to objects, you need to tell it to do that .... as in the above.
std::sort is applying its sorting to some pointer value
Let's see:
QVector<PlotColumn*> *plotMap
std::sort(plotMap->begin(), plotMap->end());
Yep, that's exactly what you told std::sort to do (assuming QVector resembles vector): you have a container of pointers, told it to sort the elements of the container. Thus, you told it to sort pointers, not PlotColumn objects.
If you want to sort the pointers based on how the objects they point to compare, you have to apply one of those
answered questions about sorting vectors of pointers to structs
You've already identified your problem:
I've tried to apply the pattern in those solutions but it still doesn't work.
Don't give up on that line of inquiry: you've correctly identified something you need to work on, and should work to understand how those patterns work and how to apply them to your problem, and maybe ask a focused question regarding your attempts at that.
It will not work because You are using the same signature. Overload works for different signatures. Check your function signatures.
Related
I am sorry if the title isn't very descriptive, I was having a hard time figuring out how to name this question. This is pretty much the first time I need to use a set, though I've been using maps forever.
I don't think it is possible, but I need to ask. I would like to perform a specific action on a struct when I add it to my std::set, but only if equality is true.
For example, I can use a list and then sort() and unique() the list. In my predicate, I can do as I wish, since I will get the result if 2 values are equal.
Here is a quick example of what my list predicate looks like:
bool markovWeightOrdering (unique_ptr<Word>& w1, unique_ptr<Word>& w2) {
if (w1->word_ == w2->word_) {
w1->weight_++;
return true;
}
return false;
}
Does anyone have an idea how to achieve a similar result, while using a std::set for the obvious gain in performance (and simplicity), since my container needs to be unique anyways? Thank you for any help or guidance, it is much appreciated.
element in set are immutable, so you cannot modify them.
if you use set with pointer (or similar), the pointed object may be modified (but care to not modify the order). std::set::insert returns a pair with iterator and a boolean to tell if element has been inserted, so you may do something like:
auto p = s.insert(make_unique<Word>("test"));
if (p.second == false) {
(*p.first)->weight += 1;
}
Live example
Manipulating a compare operator is likely a bad idea.
You might use a std::set with a predicate, instead:
struct LessWord
{
bool operator () (const std::unique_ptr<Word>& w1, const std::unique_ptr<Word>& w2) {
return w1->key < w2->key;
}
};
typedef std::set<std::unique_ptr<Word>, LessWord> word_set;
Than you test at insert if the word is existing and increment the weight:
word_set words;
std::unique_ptr<Word> word_ptr;
auto insert = words.insert(word_ptr);
if( ! insert.second)
++(insert.first->get()->weight_);
Note: Doing this is breaking const correctness, logically. A set element is immutable, but the unique_ptr enables modifications (even a fatal modification of key values).
If i define a structure
struct dat{
int a;
char b;
};
and then I declare an array of that structure, i.e
dat array[10];
and then I sort the dat array array[i].a, i.e
std::sort((array.a),(array.a+10);
will this work?
And suppose after sorting, array[5].a goes to array[2].a, does array[5].b also go to array[2].b, if not how to do this using std library function sort.
To sort your data structure using the std::sort() algorithm, you could supply a comparator function as its third argument.
For example, to sort by the values of dat.a:
bool IntSorter (const dat& dat1, const dat& dat2) { return dat1.a < dat2.a; }
Then, you call sort like this:
std::sort(array, array + 10, IntSorter);
Also, you could refactor your code to avoid the magic number 10, also to avoid duplicating it when referring to the one-past-last element in the call to std::sort().
No, it won't work as written.
First std::sort((array.a),(array.a+10); is incorrect. array.a is not an array, and trying to treat it as one will certainly lead to some problems.
You would need to instead sort the array itself (std::sort(array, array+10);), but again, that won't work because you don't provide an overload of opeartor<(dat).
You could provide one:
bool operator<(const dat& l, const dat& r)
{
return l.a < r.a;
}
Then std::sort(array, array+10); would work as expected.
When you sort an object, it "all goes together". That means that dat::a and dat::b will not be modified within a specific object, but the location of that object in the sorted array may be at a different index.
If you want to sort the array of structures according to the value of a:
std::sort(std::begin(array), std::end(array),
[](dat const & lhs, dat const & rhs){return lhs.a < rhs.a;});
This will move the b values along with their corresponding a values, which I think is what you say you want. If you want the b values to stay where they are, then it would be somewhat messier. C++ doesn't provide any way to treat an array of dat as an array of int.
For starters, array.a isn't a legal expression, since array
doesn't have an a member, and in fact, isn't even a struct.
If you want to sort the dat members by field a, you'll need
to either provide a custom ordering function (the preferred way,
especially if you have C++11 and can use lambda functions), or
define an operator< on dat. If you just want to move the
a members during the sort, leaving the b members where they
are... you'd have to define a custom swap function as well:
void
swap( dat& lhs, dat& rhs )
{
std::swap( lhs.a, rhs.a );
}
But this would be very wierd, and I'd suggest finding some other
way of organizing your data.
I used the STL map in order to keep information about my vertices and their value
and I have decided to keep them as pointers for faster run time.
The problem is that now I'm trying to add new vertices only if they don't exist, therefore I used
the MAP.FIND() function and logically its not the right thing to do.
(since the find function now only compares by the pointers values and not the vertex values)
Now after googling for a bit, I tried using the longer c-tor
struct classcomp {
bool operator() (const Vertex* v1,const Vertex* v2) const
{
return v1->getX()==v2->getX() &&
v1->getY()==v2->getY() &&
v1->getZ()==v2->getZ();
}
};
map<Vertex *,Position,classcomp> ;
but still no success , what am I doing wrong?
is it even possible to use find in a different way than comparing pointer address values?
Thanks,
-Despair
updates: still having a problem,
since you cant really use bool operator< with 2 arguments inside a struct.
so far I didnt find anything useful and all compilation tryouts failed.
couldnt find an example of doing so.
are you positive its possible with pointers?
thank you.
Your comparison function needs to impleent a strict weak ordering (operator<) not equality.
I've recently been porting a Python application to C++, but am now at a loss as to how I can port a specific function. Here's the corresponding Python code:
def foo(a, b): # Where `a' is a list of strings, as is `b'
for x in a:
if not x in b:
return False
return True
I wish to have a similar function:
bool
foo (char* a[], char* b[])
{
// ...
}
What's the easiest way to do this? I've tried working with the STL algorithms, but can't seem to get them to work. For example, I currently have this (using the glib types):
gboolean
foo (gchar* a[], gchar* b[])
{
gboolean result;
std::sort (a, (a + (sizeof (a) / sizeof (*a))); // The second argument corresponds to the size of the array.
std::sort (b, (b + (sizeof (b) / sizeof (*b)));
result = std::includes (b, (b + (sizeof (b) / sizeof (*b))),
a, (a + (sizeof (a) / sizeof (*a))));
return result;
}
I'm more than willing to use features of C++11.
I'm just going to add a few comments to what others have stressed and give a better algorithm for what you want.
Do not use pointers here. Using pointers doesn't make it c++, it makes it bad coding. If you have a book that taught you c++ this way, throw it out. Just because a language has a feature, does not mean it is proper to use it anywhere you can. If you want to become a professional programmer, you need to learn to use the appropriate parts of your languages for any given action. When you need a data structure, use the one appropriate to your activity. Pointers aren't data structures, they are reference types used when you need an object with state lifetime - i.e. when an object is created on one asynchronous event and destroyed on another. If an object lives it's lifetime without any asynchronous wait, it can be modeled as a stack object and should be. Pointers should never be exposed to application code without being wrapped in an object, because standard operations (like new) throw exceptions, and pointers do not clean themselves up. In other words, pointers should always be used only inside classes and only when necessary to respond with dynamic created objects to external events to the class (which may be asynchronous).
Do not use arrays here. Arrays are simple homogeneous collection data types of stack lifetime of size known at compiletime. They are not meant for iteration. If you need an object that allows iteration, there are types that have built in facilities for this. To do it with an array, though, means you are keeping track of a size variable external to the array. It also means you are enforcing external to the array that the iteration will not extend past the last element using a newly formed condition each iteration (note this is different than just managing size - it is managing an invariant, the reason you make classes in the first place). You do not get to reuse standard algorithms, are fighting decay-to-pointer, and generally are making brittle code. Arrays are (again) useful only if they are encapsulated and used where the only requirement is random access into a simple type, without iteration.
Do not sort a vector here. This one is just odd, because it is not a good translation from your original problem, and I'm not sure where it came from. Don't optimise early, but don't pessimise early by choosing a bad algorithm either. The requirement here is to look for each string inside another collection of strings. A sorted vector is an invariant (so, again, think something that needs to be encapsulated) - you can use existing classes from libraries like boost or roll your own. However, a little bit better on average is to use a hash table. With amortised O(N) lookup (with N the size of a lookup string - remember it's amortised O(1) number of hash-compares, and for strings this O(N)), a natural first way to translate "look up a string" is to make an unordered_set<string> be your b in the algorithm. This changes the complexity of the algorithm from O(NM log P) (with N now the average size of strings in a, M the size of collection a and P the size of collection b), to O(NM). If the collection b grows large, this can be quite a savings.
In other words
gboolean foo(vector<string> const& a, unordered_set<string> const& b)
Note, you can now pass constant to the function. If you build your collections with their use in mind, then you often have potential extra savings down the line.
The point with this response is that you really should never get in the habit of writing code like that posted. It is a shame that there are a few really (really) bad books out there that teach coding with strings like this, and it is a real shame because there is no need to ever have code look that horrible. It fosters the idea that c++ is a tough language, when it has some really nice abstractions that do this easier and with better performance than many standard idioms in other languages. An example of a good book that teaches you how to use the power of the language up front, so you don't build bad habits, is "Accelerated C++" by Koenig and Moo.
But also, you should always think about the points made here, independent of the language you are using. You should never try to enforce invariants outside of encapsulation - that was the biggest source of savings of reuse found in Object Oriented Design. And you should always choose your data structures appropriate for their actual use. And whenever possible, use the power of the language you are using to your advantage, to keep you from having to reinvent the wheel. C++ already has string management and compare built in, it already has efficient lookup data structures. It has the power to make many tasks that you can describe simply coded simply, if you give the problem a little thought.
Your first problem is related to the way arrays are (not) handled in C++. Arrays live a kind of very fragile shadow existence where, if you as much as look at them in a funny way, they are converted into pointers. Your function doesn't take two pointers-to-arrays as you expect. It takes two pointers to pointers.
In other words, you lose all information about the size of the arrays. sizeof(a) doesn't give you the size of the array. It gives you the size of a pointer to a pointer.
So you have two options: the quick and dirty ad-hoc solution is to pass the array sizes explicitly:
gboolean foo (gchar** a, int a_size, gchar** b, int b_size)
Alternatively, and much nicer, you can use vectors instead of arrays:
gboolean foo (const std::vector<gchar*>& a, const std::vector<gchar*>& b)
Vectors are dynamically sized arrays, and as such, they know their size. a.size() will give you the number of elements in a vector. But they also have two convenient member functions, begin() and end(), designed to work with the standard library algorithms.
So, to sort a vector:
std::sort(a.begin(), a.end());
And likewise for std::includes.
Your second problem is that you don't operate on strings, but on char pointers. In other words, std::sort will sort by pointer address, rather than by string contents.
Again, you have two options:
If you insist on using char pointers instead of strings, you can specify a custom comparer for std::sort (using a lambda because you mentioned you were ok with them in a comment)
std::sort(a.begin(), a.end(), [](gchar* lhs, gchar* rhs) { return strcmp(lhs, rhs) < 0; });
Likewise, std::includes takes an optional fifth parameter used to compare elements. The same lambda could be used there.
Alternatively, you simply use std::string instead of your char pointers. Then the default comparer works:
gboolean
foo (const std::vector<std::string>& a, const std::vector<std::string>& b)
{
gboolean result;
std::sort (a.begin(), a.end());
std::sort (b.begin(), b.end());
result = std::includes (b.begin(), b.end(),
a.begin(), a.end());
return result;
}
Simpler, cleaner and safer.
The sort in the C++ version isn't working because it's sorting the pointer values (comparing them with std::less as it does with everything else). You can get around this by supplying a proper comparison functor. But why aren't you actually using std::string in the C++ code? The Python strings are real strings, so it makes sense to port them as real strings.
In your sample snippet your use of std::includes is pointless since it will use operator< to compare your elements. Unless you are storing the same pointers in both your arrays the operation will not yield the result you are looking for.
Comparing adresses is not the same thing as comparing the true content of your c-style-strings.
You'll also have to supply std::sort with the neccessary comparator, preferrably std::strcmp (wrapped in a functor).
It's currently suffering from the same problem as your use of std::includes, it's comparing addresses instead of the contents of your c-style-strings.
This whole "problem" could have been avoided by using std::strings and std::vectors.
Example snippet
#include <iostream>
#include <algorithm>
#include <cstring>
typedef char gchar;
gchar const * a1[5] = {
"hello", "world", "stack", "overflow", "internet"
};
gchar const * a2[] = {
"world", "internet", "hello"
};
...
int
main (int argc, char *argv[])
{
auto Sorter = [](gchar const* lhs, gchar const* rhs) {
return std::strcmp (lhs, rhs) < 0 ? true : false;
};
std::sort (a1, a1 + 5, Sorter);
std::sort (a2, a2 + 3, Sorter);
if (std::includes (a1, a1 + 5, a2, a2 + 3, Sorter)) {
std::cerr << "all elements in a2 was found in a1!\n";
} else {
std::cerr << "all elements in a2 wasn't found in a1!\n";
}
}
output
all elements in a2 was found in a1!
A naive transcription of the python version would be:
bool foo(std::vector<std::string> const &a,std::vector<std::string> const &b) {
for(auto &s : a)
if(end(b) == std::find(begin(b),end(b),s))
return false;
return true;
}
It turns out that sorting the input is very slow. (And wrong in the face of duplicate elements.) Even the naive function is generally much faster. Just goes to show again that premature optimization is the root of all evil.
Here's an unordered_set version that is usually somewhat faster than the naive version (or was for the values/usage patterns I tested):
bool foo(std::vector<std::string> const& a,std::unordered_set<std::string> const& b) {
for(auto &s:a)
if(b.count(s) < 1)
return false;
return true;
}
On the other hand, if the vectors are already sorted and b is relatively small ( less than around 200k for me ) then std::includes is very fast. So if you care about speed you just have to optimize for the data and usage pattern you're actually dealing with.
This is a rather simple question, and I think it breaks down to whether or not this is legal. I have a class with the ">" and "<" operators overloaded. I'm trying to make a single statement that will return true or false without having to declare an iterator for the class, just for "simplicity".
Here is an example of what I'm talking about:
class Guy
{
double bigness; //I'm not illiterate by the way
public:
bool operator>(const Guy& that) {return (this->bigness > that.bigness);}
};
Ok, this right there should work. Now I make two vectors of type guy, and I want to compare two members in a single expression so I get the truth value for that expression, like this:
vector<Guy> gang, mafia;
bool mafiaWins = (*(mafia.begin()) > *(gang.begin()));
I didn't populate the vector, so I wouldn't get anything reasonable from this code. But how could I manage to have the expression (mafia.begin() > gang.begin()) to work? I know they are both returning iterators; but dereferencing them doesn't work. Using .front() gives me the same error. I get an assertion for having a non-dereferencable iterator (Shouldn't it be dereferenciable?). I'm wondering if the problem is on not storing it in an iterator first...
Thanks for your attention
Solution:
So, the code I have above (Edited from when I first posted), should work for the expression. That is not the code I actually had in my .cpp, that's why its so messy. You don't need to use random access reference if you are only trying to reach the first member of the vector, and ,front() will the the reference for the first term. I originally left the vector empty in this post, because I thought it was irrelevant to populate them, and by doing that I accidentally got what my problem was in my code. The function I had getting vectors and checking their members was not checking whether or not the members I was comparing existed. So, for anyone that gets a similar assertion problem, you are possibly trying to dereference something that doesn't exist. For checking if a vector is empty there is a member function that can be used.
Example:
bool thereIsWar = (!(mafia.empty() && !(gang.empty()));
if(thereIsWar)
bool mafiaWins = (*(mafia.begin()) > *(gang.being()));
Hope it helps anyone with a similar question
that is a reference.
Change:
bool operator>(const Guy& that) {return (this->bigness > that->bigness);}
To:
bool operator>(const Guy& that) {return (this->bigness > that.bigness);}
Ensure your vector has at least one element before testing:
vector<Guy> gang, mafia;
gang.push_back(Guy()); // Not sure you how you set 'bigness'.
mafia.push_back(Guy());
Dereference iterators for comparision:
bool mafiaWins = (*mafia.begin() > *gang.begin()) ;
This should work so long as the vector has contents (I get a segfault if they are empty).
bool mafiaWins = (*mafia.begin()) > (*gang.begin());
as should
bool mafiaWins = mafia.front() > gang.front();
If it doesn't fix please post the exact error.
NB: the less than/greater than operator are defined for random access iterator for comparing position.