I have some problem with sorting my vectors.I want to sort my vectors acording to first element wchich is Number.Can someone explain me what I doing wrong and what this 2 errors means?
I try sort this with compare function and without it and nothing works.
struct Point{
int x;
int y;
};
bool compare(int a,int b){
return a < b;
}
int main()
{
int N,Number,x,y;
cin >> N;
vector<pair<int,Point>> p;
for(int i = 0 ; i < N ; i++){
cin >> Number >> x >> y;
pair<int,Point> pom = {Number,{x,y}};
p.push_back(pom);
}
// sort(p.begin(),p.end());
// sort(p.begin().p.end(),compare);
return 0;
}
I've got two errors but I don't know what means:
1.no match for 'operator<' (operand types are 'const Point' and 'const Point')
|| (!(__y.first < __x.first) && __x.second < __y.second); }
2.body of constexpr function 'constexpr bool std::operator<(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&) [with _T1 = int; _T2 = Point]' not a return-statement
|| (!(__y.first < __x.first) && __x.second < __y.second); }
^
^
Comparator for ints is useless and probably Undefined Behaviour. Compiler knows how to compare two integers, it's part of the language. It also knows how to compare two instances of std::pair. What it doesn't know, is how to compare two instances of Point class, which is defined by you.
Basically, you have two options:
1. Provide operator< for Point
This will let you compare two points easily in any situation later on:
bool operator<(const Point& p1, const Point& p2) {
if (p1.x == p2.x)
{
return p1.y < p2.y;
}
return p1.x < p2.x;
}
2. Provide an in-place comparator for std::sort
This is a quick solution if you only need to compare stuff for sorting purposes.
std::sort(p.begin(), p.end(), [](const auto& p1, const auto& p2) {
//whatever logic it takes to compare two std::pair<int, Point>
});
Note: Generic lambda (with const auto& as arguments) is a C++14 feature. Lambdas themselves are C++11 feature.
Choice between above depends on the usage. If you just need to sort a vector, of if the logic for sorting is unusual, go for std::sort comparator. If you want to always compare two Points in the same way, go for operator overloading.
Related
I have some pretty simple C++ code that creates points from a struct definition and tries to add those points to a set.
#include <stdio.h> /* printf */
#include <bits/stdc++.h> /* vector of strings */
using namespace std;
struct point
{
int x;
int y;
};
int main(){
for(int i = 0; i <= 6; i++){
set<point> visited_points;
point visited_point{4, 1};
visited_points.insert(visited_point);
}
}
But this code throws a large console error when I run it saying:
In file included from /usr/include/c++/7/string:48:0,
from /usr/include/c++/7/bits/locale_classes.h:40,
from /usr/include/c++/7/bits/ios_base.h:41,
from /usr/include/c++/7/ios:42,
from /usr/include/c++/7/istream:38,
from /usr/include/c++/7/sstream:38,
from /usr/include/c++/7/complex:45,
from /usr/include/c++/7/ccomplex:39,
from /usr/include/x86_64-linux-gnu/c++/7/bits/stdc++.h:52,
from ex.cpp:2:
/usr/include/c++/7/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = point]’:
/usr/include/c++/7/bits/stl_tree.h:2038:11: required from ‘std::pair<std::_Rb_tree_node_base*, std::
........
/usr/include/c++/7/bits/stl_function.h:386:20: note: ‘const point’ is not derived from ‘const std::__cxx11::sub_match<_BiIter>’
{ return __x < __y; }
~~~~^~~~~
Is there a part of my code that I did wrong? I just want a way to keep track of multiple points in a list.
Sets are ordered, and so the elements need an ordering function. Your point class doesn't not have this. Add a suitable definition of
bool operator<(const Point& a, const Point& b);
For instance
bool operator<(const Point& a, const Point& b)
{
return a.x < b.x || a.x == b.x && a.y < b.y;
}
But whatever ordering function you choose it must define a strict weak ordering
I am new to C++ and am trying to sort a vector based on values in another vector. I am trying to implement this by creating a vector of structs and sorting the vector of stucts using the STL. The structs have 2 data items, one a CustomType and the other an int. I want this sorted in decreasing order of the int field, and have therefore included a boolean operator overloading to be able to use the STL sort (algorithm).
The struct is constructed in the function using references to the CustomType vector and an initially uninitialised int vector, and combining them into a vector of structs. The values for the ints are obtained by calling a separate member function of SomeClass (SomeFunc) on each item of the CustomType vector and another u_int8_t param (this function works fine by itself).
In the end, I want to replace the sorted CustomType objects based on the sorted struct sequence.
The implementation file (.cpp) has the following function:
void SomeClass::orderFunc(std::vector<CustomType>& x, std::vector<int>& y, u_int8_t param){
std::vector<CustomStruct> xy_vec;
y.assign(x.size(), 0);
int count = int(x.size());
for(int i=0; i != count; ++i){
y[i] = SomeFunc(x[i], param);
}
for(int i = 0; i != count; ++i){
xy_vec[i].var1 = x[i];
xy_vec[i].var2 = y[i];
}
std::sort(xy_vec.begin(), xy_vec.end());
for(int i = 0; i != count; ++i){
x[i] = xy_vec[i].var2;
}
}
The struct is defined in the SomeClass header file as below:
struct CustomStruct{
CustomType var1;
int var2;
bool operator>(const CustomStruct& a) const{
return (this->var2 > a.var2);
}
};
When this function is called, I get the following error:
invalid operands to binary expression
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
I can't understand why the bool operator overloading is invalid given that this is being defined for the int field of the struct.
What am I missing? Any help would be appreciated. Also, any suggestions for a more elegant way to do this would be great as well.
You need to overload operator< and not operator>
bool operator<(const CustomStruct& a) const
{
return (this->var2 < a.var2);
}
EDIT: For sorting in reverse order, you need to call std::sort with rbegin() and rend() (reverse) iterators:
std::sort(xy_vec.rbegin(), xy_vec.rend());
EDIT (again, as the question is too long, has 2 problems):
The vector xy_vec is empty, you need to call resize:
std::vector<CustomStruct> xy_vec;
// Resize here
xy_vec.resize(count);
for(int i = 0; i != count; ++i){
xy_vec[i].var1 = x[i];
xy_vec[i].var2 = y[i];
Or you can call push_back - I am not telling you all that. Please find!
std::sort has two main overloads, one without a sort predicate that defaults to using operator < and one with a sort predicate (details here).
So you could write something like
struct CustomStructCmp {
bool operator()(const CustomStruct& a, const CustomStruct& b) const
{
return a.var2 > b.var2;
}
};
std::sort(xy_vec.begin(), xy_vec.end(), CustomStructCmp());
(if you are using C++11 then you could use a lambda instead).
Alternatively you could write
std::sort(xy_vec.begin(), xy_vec.end(), std::greater<CustomStruct>());
but I feel that it is more natural to directly use a functor/lambda rather than define operator> and use the std::greater functor.
I already have a workaround for this but would still like to understand the problem.
I have a Multimap
multimap<QPoint,Figure*> mymap;
QPoint is a class from Qt 5.4. Figure* is a pointer to a
class I have concocted myself.
Now I want to add elements to that map.
This sample works fine:
multimap<int,int> test;
test.insert(pair<int,int>(41,43));
As does this one (being said workaround)
std::pair<QPoint,Figure*> p;
p.first = pos;
p.second = sub_fig;
mymap.insert(p);
However, the plain first reflex of
std::pair<QPoint,Figure*> p(pos, sub_fig);
has the compiler at that line state something like:
[..]
scanner.cpp:264:17: required from here
/usr/include/c++/4.9/bits/stl_function.h:371:20: error: no match for
‘operator<’ (operand types are ‘const QPoint’ and ‘const QPoint’)
{ return __x < __y; }
[..]
followed by the usual five kilometers of stacked STL error messages.
First: The 'types' are not QPoint and QPoint. They are, as
stated above, QPoint and Figure*.
Anyone who can riddle this?
CORRECTION
My work-around does not work either after all. I had forgotten to
de-comment
res.insert(p);
Here is the complete pertinent code:
multimap<QPoint,Figure*> res;
// ...
vector<Figure*> stack = figure->get_above_figure_stack();
for (vector<Figure*>::const_iterator CI2=stack.begin();
CI2!=stack.end();CI2++)
{
// ..
Figure* sub_fig = *CI2;
std::pair<QPoint,Figure*> p;
p.first = pos;
p.second = sub_fig;
res.insert(p); // <- The bad line.
}
multimap needs an ordering relation for the keys, and its default is to use < (in the guise of std::less).
Since QPoint doesn't have an overload of operator<, the compiler is complaining that it doesn't exist.
It's not difficult to provide one:
bool operator< (const QPoint& lhs, const QPoint& rhs)
{
return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y());
}
or
bool lessQPoints (const QPoint& lhs, const QPoint& rhs)
{
return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y());
}
multimap<QPoint, Figure*, lessQPoints> mymap;
The keys in a multimap are ordered by default with std::less which invokes operator< on the key type.
Your key object (QPoint) has no operator< to do the comparison.
You will need to provide your own comparison function using the approprate multimap constructor.
Without going into much details why I´m doing what I´m doing let me describe the issue.
Im using a std::set for storing unique objects of a struct called VertexTypePos3Normal.
The struct is defined as following:
struct VertexTypePos3Normal {
// ctor, dtor ..
friend bool operator==(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2);
friend bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2);
glm::vec3 pos;
glm::vec3 normal;
};
bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2) {
return (v1.pos.x < v2.pos.x) && (v1.pos.y < v2.pos.y) && (v1.pos.z < v2.pos.z) && (v1.normal.x < v2.normal.x) && (v1.normal.y < v2.normal.y) && (v1.normal.z < v2.normal.z);
}
// operator == ommited
Per default std::set uses std::less as comparison function.
So I first declared my set as std::set<VertexTypePos3Normal> set;
The elements inserted into the set are stored in a std::vector that is not containing unique values (looping over the vector).
Using std::less called my operator< but the result was not correct as the set contained mostly only 1 value although the vector contained about 15 different ones.
Here is the method inserting into the set:
void createUniqueVertices(const std::vector<const VertexTypePos3Normal>& verticesIn,
std::vector<const VertexTypePos3Normal>& verticesOut,
std::vector<unsigned short>& indicesOut)
{
//std::map<VertexTypePos3Normal, int, std::equal_to<VertexTypePos3Normal> > map;
std::set<const VertexTypePos3Normal, std::equal_to<const VertexTypePos3Normal> > set;
int indexCounter = 0;
for (auto c_it = verticesIn.cbegin(); c_it != verticesIn.cend(); ++c_it) {
//bool newlyAdded = map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter)).second;
bool newlyAdded = set.insert(*c_it).second;
//if (newlyAdded) {
//verticesOut.push_back(*c_it);
//map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter));
//++indexCounter;
//}
//indicesOut.push_back(map[*c_it]);
}
}
So I was about to try out std::equal_to instead of std::less and wrote operator==.
Now the weird stuff started:
Although I´m not calling std::less anymore and therefore also not operator<, there is an assertion error in STL (using VC compiler) _DEBUG_ERROR2("invalid operator<", _File, _Line);
So actually i got two questions:
1.) Why is my operator < not working with std::less as it is supposed to.
2.) How can operator< trigger an assertion when it is not even called.
EDIT: Thanks for all information. Looks like I totally missunderstood strict weak ordering. Using std::tie taking care of it solved my problem. Here is the updated code:
void createUniqueVertices(const std::vector<const VertexTypePos3Normal>& verticesIn,
std::vector<const VertexTypePos3Normal>& verticesOut,
std::vector<unsigned short>& indicesOut)
{
std::map<VertexTypePos3Normal, int> map;
int indexCounter = 0;
for (auto c_it = verticesIn.cbegin(); c_it != verticesIn.cend(); ++c_it) {
bool newlyAdded = map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter)).second;
if (newlyAdded) {
verticesOut.push_back(*c_it);
//map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter));
++indexCounter;
}
indicesOut.push_back(map[*c_it]);
}
}
Im using a map in the final version as the set is obsolete.
Here is my new operator<
bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2) {
return (std::tie(v1.pos.x, v1.pos.y, v1.pos.z, v1.normal.x, v1.normal.y, v1.normal.z) < std::tie(v2.pos.x, v2.pos.y, v2.pos.z, v2.normal.x, v2.normal.y, v2.normal.z));
}
Ordered associative containers require a strict weak ordering relation. Among the required properties is antisymmetry, that is, cmp(x,y) implies !cmp(y,x). Your definition of operator< does not satisfy this property.
Also, equality (or equivalence) may be defined as !(cmp(x,y)||cmp(y,x)), and often this is used instead of x==y. That is, operator< may be called even if you don't use it explicitly.
Your operator < is plain wrong.
You might want:
bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2) {
if(v1.pos.x < v2.pos.x) return true;
else if(v1.pos.x == v2.pos.x) {
if(v1.pos.y < v2.pos.y) return true;
else if(v1.pos.y == v2.pos.y) {
if(v1.pos.z < v2.pos.z) return true;
else if(v1.pos.z < v2.pos.z) {
if(v1.normal.x < v2.normal.x) return true;
else if(v1.normal.x == v2.normal.x) {
if(v1.normal.y < v2.normal.y) return true;
else if(v1.normal.y < v2.normal.y) {
if(v1.normal.z < v2.normal.z) return true;
}
}
}
}
}
return false;
}
Note: That should be split into two less function calls for glm::vec3 (having bool less(const glm::vec3&, const glm::vec3&);)
I attempted to do something like this but it does not compile:
class point
{
public:
int x;
int y;
};
int main()
{
vector<point> vp1;
vector<point> vp2;
vector<point> vp3;
map < vector<point>, int > m;
m[vp1] = 1;
m[vp2] = 2;
m[vp3] = 3;
map < vector<point>, int >::iterator it;
for (it=m.begin(); it!=m.end(); it++)
{
cout<<m[it->first]<<endl;
}
return 0;
}
You can use anything as the index type into a std::map as long as it supports an operator< (which could could define as a free-standing function -- doesn't have to be a member function, as long as you can write a < b for a and b being instances of your type of interest) with the usual semantics (antireflexive, transitive, ...). Or, you can pass a binary function with the same semantics to use in lieu of <, if that suits you better.
You can, but the type used as a key in a map needs to be comparable, either using operator<, or using a comparison function/functor you supply as the third template parameter for the map type.
Yes, you can. Vectors, like all containers, are comparable. The resulting map will sort the vectors in lexicographic order.
The problem is that point is not comparable. You have to define a sort order for points, and then this will in turn define lexicographic order over vector<point>.
class point
{
public:
int x;
int y;
};
bool operator<( point const &l, point const &r ) {
return l.x < r.x? true
: r.x < l.x? false
: l.y < r.y;
}
A simpler solution is to use std::pair instead of defining your own point.
typedef pair< int, int > point; // point::first = x, point::second = y
// pair is already comparable; order defined as in previous example
typedef vector<point> pointvec; // OK
You haven't defined a function to compare a vector<point> Maps make requiremets of keys checking of equivalence and comparison.
You have to declare the operator<. It would look like this (please keep in mind, that the three vectors in your sample code actually look the same):
bool operator<(const vector<point>& left, const vector<point>& right)
{
return left.size() < right.size();
}