STL Sort on nested Classes - c++

I have a graph class that has a vector of nodes. In each node, there is a vertex and a STL list of edges. Essentially it's an adjacency list.
For my assignment, I am trying to display the vertices with the most edges. I figured I would sort the graph's vector of nodes by edge size and print the top N vertices.
So I am trying to figure out STL sort, but I'm having difficulties.
I have
std::sort(path.begin(), path.end());
Where path is the vector of nodes (node = vertex value and list of edges)
In my node class, I have
bool operator<(const Node<T>& rhs){
return size < rhs.size; //size is how many edges the vertex has
}
But it's giving me errors. How can I construct the operator< function in my node class to work with STL sort?
Here are the errors:
$ make
g++ -c -g -std=c++0x graphBuilder.cpp
In file included from /usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/algorithm:63:0,
from graph.h:6,
from graphBuilder.cpp:2:
/usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/bits/stl_algo.h: In function '_RandomAccessIterator std::__unguarded_partition(_RandomAccessIterator, _RandomAccessIterator, const _Tp&) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Node<std::basic_string<char> >*, std::vector<Node<std::basic_string<char> >, std::allocator<Node<std::basic_string<char> > > > >, _Tp = Node<std::basic_string<char> >]':
/usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/bits/stl_algo.h:2249:70: instantiated from '_RandomAccessIterator std::__unguarded_partition_pivot(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Node<std::basic_string<char> >*, std::vector<Node<std::basic_string<char> >, std::allocator<Node<std::basic_string<char> > > > >]'
/usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/bits/stl_algo.h:2280:54: instantiated from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Node<std::basic_string<char> >*, std::vector<Node<std::basic_string<char> >, std::allocator<Node<std::basic_string<char> > > > >, _Size = int]'
/usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/bits/stl_algo.h:5212:4: instantiated from 'void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<Node<std::basic_string<char> >*, std::vector<Node<std::basic_string<char> >, std::allocator<Node<std::basic_string<char> > > > >]'
graph.h:32:13: instantiated from 'void Graph<T>::topN(int) [with T = std::basic_string<char>]'
graphBuilder.cpp:10:17: instantiated from here /usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/bits/stl_algo.h:2211:4: error: passing 'const Node<std::basic_string<char> >' as 'this' argument of 'bool Node<T>::operator<(const Node<T>&) [with T = std::basic_string<char>]' discards qualifiers
make: *** [graphBuilder.o] Error 1

Your member function needs to be const-qualified:
bool operator<(const Node<T>& rhs) const{
EDIT
Per request, here is a bit more how I knew that you needed to make the member function const.
Divining the hidden meanings in stdlib-related compiler errors is something of an art, and something that you get better at with practice. Stdlib-related errors will often emit a whole series of compiler errors. Usually the most useful of these errors is the last one, because that one is generated from the context of the code you actually wrote, rather than coming from the bowels of the library code.
In this case, the last compiler error was:
graphBuilder.cpp:10:17: instantiated from here
/usr/lib/gcc/i486-slackware-linux/4.5.2/../../../../include/c++/4.5.2/bits/stl_algo.h:2211:4:
error: passing 'const Node >' as 'this'
argument of 'bool Node::operator<(const Node&) [with T =
std::basic_string]' discards qualifiers
I've highlighted the illuminating bits. This tells me that in OP's actual code, because of how the code is constructed (maybe the sort call is itself in a const member function? who knows...) the this pointer must be const, but as we can see from the posted declaration of operator<, the method is not const. The "qualifiers" referred to in the compiler errors are what the Standard calls "cv-qualifiers". "cv" stands for "const/volatile."
When the compiler says "Passing const X as this to Node::operator< discards qualifiers" what it's really trying to say is:
"You said X was const but then you tried to call a non-const member function through const X. In order for me to make this call, I would have to discard the const qualifier on X. I'm not allowed to do that, so you have to fix your code."
The qualifiers being "discarded" here are the qualifiers on the method itself. In other words, operator< must be a const member function, but it's not.

bool operator<(const Node<T>& rhs) const {
return size() < rhs.size();
}
Making the operator const as it should be may help with not angering the compiler.
Note: The problem arises because this is always const so the compiler expects you to promise not to change it which you specify by making the method using this to have the const qualifier.
Alternately you could make a non-member comparison for use with sort like so:
<template typename T>
struct CompareNodes {
bool operator()(const Node<T>& lhs, const Node<T>& rhs) const
{
return lhs.size() < rhs.size();
}
};
std::sort(path.cbegin(), path.cend(), CompareNodes<T>());

Related

Why constant objects can't be replaced in a container? [duplicate]

How to push_back() to a C++ std::vector without using operator=() for which the default definition violates having const members?
struct Item {
Item(int value)
: _value(value) {
}
const char _value;
}
vector<Item> items;
items.push_back(Item(3));
I'd like to keep the _value const since it should not change after the object is constructed, so the question is how do I initialize my vector with elements without invoking operator=()?
Here is the basic error the g++ v3.4.6 is giving me:
.../3.4.6/bits/vector.tcc: In member function `Item& Item::operator=(const Item&)':
.../3.4.6/bits/vector.tcc:238: instantiated from `void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = Item, _Alloc = std::allocator<Item>]'
.../3.4.6/bits/stl_vector.h:564: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = Item, _Alloc = std::allocator<Item>]'
item.cpp:170: instantiated from here
.../3.4.6/bits/vector.tcc:238: error: non-static const member `const char Item::_value', can't use default assignment operator
For std::vector<T> the elements are required to be Assignable. You type is not Assignable. An implementation of. std::vector<T> could avoid insisting on this requirement but this would be a disservice as the resulting code wouldn't be portable.
You can use a std::list<T> instead or change the definition of you type. For example you can create an accessor to only read the value but no setter. Of course, assignment would change the value. The choice thus is to either allow this change or allow putting the objects into a container. You won't get both.

Errors risen on std::find over a vector of objects

In this function I would like to search 'vertex' in the vector queue.
bool PriorityQueue::contains(VertexPriority vertex) const {
return (std::find(queue.begin(), queue.end(), vertex) != queue.end());
}
The vector queue is an instance of this object:
std::vector<VertexPriority> queue;
And my operator overloading is this:
bool operator==(const VertexPriority& v){ return (v.vertex == vertex); }
How can I solve this error?
The errors risen are the next ones, and at the beginning of every error there is the following path:
C:\Dev-Cpp\include\c++\3.4.2\bits\stl_algo.h
In function `_RandomAccessIterator std::find(_RandomAccessIterator, _RandomAccessIterator, const _Tp&, std::random_access_iterator_tag) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator > >, _Tp = VertexPriority]':
instantiated from `_InputIterator std::find(_InputIterator, _InputIterator, const _Tp&) [with _InputIterator = __gnu_cxx::__normal_iterator > >, _Tp = VertexPriority]'
passing const VertexPriority' asthis' argument of `bool VertexPriority::operator==(const VertexPriority&)' discards qualifiers
Make the operator const:
bool operator==(const VertexPriority& v) const
{
return (v.vertex == vertex);
}
Note that this makes the member function const itself, which enables the compiler to invoke it on a const object of VertexPriority type.
This is actually precisely what the compiler sais:
passing const VertexPriority' as this' argument of `bool VertexPriority::operator==(const VertexPriority&)' discards qualifiers
The VertexPriority object that is const, is being passed as the implicit thisargument to member functions.

Why I can't define compare without constant

If I define my compare function like this:
bool compare(Student& a, Student& b)
{
return a.n < b.n;
}
g++ will complain:
g++ -Wall main.cpp -o main
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/algorithm:63:0,
from main.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/bits/stl_algo.h: In function ‘_RandomAccessIterator std::__unguarded_partition(_RandomAccessIterator, _RandomAccessIterator, const _Tp&, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Student*, std::vector<Student> >, _Tp = Student, _Compare = bool (*)(Student&, Student&)]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/bits/stl_algo.h:2261:78: instantiated from ‘_RandomAccessIterator std::__unguarded_partition_pivot(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Student*, std::vector<Student> >, _Compare = bool (*)(Student&, Student&)]’
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/bits/stl_algo.h:2302:62: instantiated from ‘void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Student*, std::vector<Student> >, _Size = long int, _Compare = bool (*)(Student&, Student&)]’
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/bits/stl_algo.h:5250:4: instantiated from ‘void std::sort(_RAIter, _RAIter, _Compare) [with _RAIter = __gnu_cxx::__normal_iterator<Student*, std::vector<Student> >, _Compare = bool (*)(Student&, Student&)]’
main.cpp:38:51: instantiated from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/bits/stl_algo.h:2229:4: error: invalid initialization of reference of type ‘Student&’ from expression of type ‘const Student’
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/include/g++-v4/bits/stl_algo.h:2232:4: error: invalid initialization of reference of type ‘Student&’ from expression of type ‘const Student’
Compilation exited abnormally with code 1 at Mon May 28 08:05:35
But if I define the compare with const type, it will compile and works fine.
And here is all the code:
class Student {
public:
Student(string);
string n;
};
bool compare(Student& a, Student& b)
{
return a.n < b.n;
}
Student::Student(string name) { n = name; }
int main()
{
Student A = Student("A");
Student B = Student("B");
vector<Student> students;
students.push_back(B);
students.push_back(A);
sort(students.begin(), students.end(), compare);
cout << "After sort" << endl;
for(vector<Student>::iterator i = students.begin(); i != students.end(); ++i) {
cout << "Student: " << i->n << endl;
}
return 0;
}
In this implementation, std::sort uses
const _Tp& std::__median(const _Tp&, const _Tp&, const _Tp&, _Compare);
In your case, _Tp is student, and _Compare is compare.
So you basically have
const Student& std::__median(const Student&, const Student&, const Student&,
bool (*)(Student&, Student&) )
or similar. Obviously, the callback can't be applied to the parameters are they are converted to const, so the failure.
Make the parameters to your compare method const.
I don't believe there is a requirement in the standard that says the parameters to the function must be const, so I believe your implementation is in error for rejecting it. However, there is a requirement that the function not modify the arguments:
From the standard -- 25.4/2
Compare is a function object type (20.8). The return value of the
function call operation applied to an object of type Compare, when
contextually converted to bool (4), yields true if the first argument
of the call is less than the second, and false otherwise. Compare comp
is used throughout for algorithms assuming an ordering relation. It is
assumed that comp will not apply any non-constant function through the
dereferenced iterator.
And the signature of std::sort from 25.4.1.1
template<class RandomAccessIterator, class Compare>
void sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp)
So since your function is not allowed to modify its arguments, it really should be taking them in as const, but the standard does not require that. So while your implementation may be in error, I believe it is a forgivable error, as it manages to call attention to the fact that either your function is in violation of the standard by modifying its arguments, or it is not const-correct.
You should make it const since it leaves the two parameters the same.

populating vector into map

I´m trying to populate a Point map with a vector of Points. I´m trying to make a board game where each position on the board has a point (x,y) and vector of legal moves (Point objects).
I can´t seem to be able to have a map KEY as a Point.
struct Point
{
Point() {}
Point(int ix, int iy ) :x(ix), y(iy) {}
int x;
int y;
};
Point p_source (2,2);
Point p_next1 (1,2);
Point p_next2 (1,3);
Point p_next3 (1,4);
map <Point, vector<Point> > m_point;
dict[p_source].push_back(p_next1);
dict[p_source].push_back(p_next2);
dict[p_source].push_back(p_next3);
This is the errors that I get
In member function 'bool std::less<_Tp>::operator()(const _Tp&, const
_Tp&) const [with _Tp = Point]':|
instantiated from '_Tp& std::map<_Key, _Tp, _Compare,
_Alloc>::operator[](const _Key&) [with _Key = Point, _Tp = std::vector,
std::allocator >, std::allocator, std::allocator > > >, _Compare =
std::less, _Alloc = std::allocator,
std::allocator >, std::allocator, |
instantiated from here|
c:\program files ( no match for 'operator<' in '__x < __y'| ||===
Build finished: 1 errors, 0 warnings ===|
Checking my favourite online reference it reads:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
Map is an associative container that contains a sorted list of unique
key-value pairs. That list is sorted using the comparison function
Compare applied to the keys. Search, removal, and insertion operations
have logarithmic complexity. Maps are usually implemented as red-black
trees.
Since you don't provide an explicit Compare it sorts using the default std::less<Key>. Seems like we're on the right track because the errors are in that class:
In member function 'bool std::less<_Tp>::operator()(const _Tp&, const
_Tp&) const [with _Tp = Point]':|
Let's check that:
template< class T >
struct less;
Function object for performing comparisons. Uses operator< on type T.
This matches what the error messages tell us:
no match for 'operator<' in '__x < __y'
Hmm, but there's no operator< for type Point...
Your error is entirely unrelated to std::vector<> – std::map<> requires that its key either be comparable with operator<, or that you supply a custom comparitor. The easiest solution is to add the following after Point's definition:
bool operator <(Point const& lhs, Point const& rhs)
{
return lhs.y < rhs.y || lhs.y == rhs.y && lhs.x < rhs.x;
}

How can I insert a structure as key into a map?

I'm getting a compiling error for the code below, after removing the comment characters from the first insert line. I'm unable to insert the structure into the map while inserting integers is fine.
# include <iostream>
# include <map>
using namespace std;
struct node
{int test;} temp;
int main()
{
temp.test = 24;
int test = 30;
map<node, bool> mymap1;
map<int, bool> mymap2;
//mymap1.insert(make_pair(temp, true));
mymap2.insert(make_pair(test, true));
return 0;
}
How can I fix the error?
For a type to serve as the key for a map, it has to be ordered. All that means, practically, is that operator< must be defined for the type. If you defined a global operator<(const node&, const node&), this should work fine; i.e.,
bool operator<(const node& n1, const node& n2) {
return n1.test < n2.test;
}
A std::map's keys are stored internally in a binary search tree. In order for keys to be stored and searched for in a binary search tree, they must be comparable. For example, a requirement of a binary search tree is that a left child's key is less than its parent's key and a right child's key is greater than its parent's key. However, if the keys are not comparable, how are we supposed to tell whether the children are greater or less than the parent? We cannot form a tree and therefore std::map will not work with these types.
You simply need to define the less than operator like so:
bool operator<(const node& n1, const node& n2)
{
return n1.test < n2.test;
}
This also must be a friend of your node struct if the "test" data member is private (It's public now since node is currently a struct). However, I would probably do it like this:
#include <map>
class node
{
public:
int getTest() const { return _test; }
void setTest(int test) { _test = test; }
private:
int _test;
};
bool operator<(const node& n1, const node& n2)
{
return n1.getTest() < n2.getTest();
}
int main()
{
std::map<node,bool> foo;
node n;
n.setTest(25);
foo[n] = true;
return 0;
}
Here's how to read the error messages:
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_tree.h:1141: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = node, _Val = std::pair<const node, bool>, _KeyOfValue = std::_Select1st<std::pair<const node, bool> >, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_map.h:469: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = node, _Tp = bool, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
prog.cpp:15: instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
First, we ignore most of the 'instantiated from' lines, because they're just talking about how the templates got expanded. The important one is the last one, referring to our source code, because it tells us where the error was triggered. Of course, we knew that anyway, so we'll skip that too. We'll also ignore the path to the library header in question, because we don't really care how the compiler stores its stuff.
stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
So... our code indirectly calls ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’, or if we actually do that substitution, ‘bool std::less<node>::operator()(const node&, const node&) const’. And this is a problem because there is no match for ‘operator<’ in ‘__x < __y’.
__x and __y are variables inside the std::less implementation (you should be able to guess that much). From the name, we can guess (and if we had studied the standard library, we would know) that std::less is a template function that compares two things of the same type and returns whether or not the first is less than the second.
How does it do that? By using the operator<, of course. So that's what we need to do to fix the problem: it says the operator< isn't there for what's being compared, so we have to provide it. What's being compared? nodes, of course. So we define operator< for our class.
Why does it do that? So that we can write functions that accept a comparison-operation as an argument (either a template argument or a run-time parameter - but the former is far more common), and pass std::less. That's the reason for std::less's existence: it turns the act of comparing things into a function, and actual functions are somewhat more useful.
How is that relevant? Because, like the others said, std::map actually is passing std::less as an argument. It's actually a default argument to the std::map template that's used to compare elements. After all, part of the interface of a map is that every key is unique. How are you going to check keys for uniqueness if you can't compare them? Granted, technically you would only have to compare them for equality for that to work. But it turns out that being able to order the keys makes it possible to create a much more efficient data structure. (You would know about this if you actually took courses in university about programming and CS.)
Why wasn't there a problem with int? You should be able to guess by now: operator< already naturally works for ints. But you have to tell C++ how to do it for any user types, because you might have something else in mind.
C++11
As mentioned in Andrew Rasmussen's answer, the keys of a std::map must be comparable. However, you can also provide a custom comparison object to your map instead of defining operator< for your struct. Moreover, since C++11, you can use a lambda expression instead of defining a comparison object. As a result, you can keep your code as short as follows:
auto comp = [](const node& n1, const node& n2) { return n1.test < n2.test; };
std::map<node, bool, decltype(comp)> mymap1(comp);
Code on Ideone