Finding a vector of struct within a set - c++

I am trying to find vector of a struct within a set. In order to do so I wrote the following code:
#include <cstdlib>
#include <iostream>
#include <set>
#include <vector>
using namespace std;
struct A{
int a;
};
int main(int argc, char** argv) {
std::set< std::vector<A> > aObj;
A a1,a2,a3,a4,a5,a6;
a1.a=5; a2.a=8; a3.a=10;
a4.a=5; a5.a=8; a6.a=10;
vector<A> vecA,vecB;
vecA.push_back(a1); vecA.push_back(a2); vecA.push_back(a3);
aObj.insert(vecA);
set< vector<A> >::iterator it = aObj.find(vecB);
if (it != myset.end()) {
cout<<"\n Found vector B. \n";
}
return 0;
}
However, the above code is giving me the following error:
/usr/include/c++/4.6/bits/stl_algobase.h:881:6: error: no match for ‘operator<’ in ‘* __first1 < * __first2’
/usr/include/c++/4.6/bits/stl_algobase.h:881:6: note: candidates are:
/usr/include/c++/4.6/bits/stl_pair.h:207:5: note: template<class _T1, class _T2> bool std::operator<(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)
/usr/include/c++/4.6/bits/stl_iterator.h:291:5: note: template<class _Iterator> bool std::operator<(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_Iterator>&)
/usr/include/c++/4.6/bits/stl_iterator.h:341:5: note: template<class _IteratorL, class _IteratorR> bool std::operator<(const std::reverse_iterator<_IteratorL>&, const std::reverse_iterator<_IteratorR>&)
/usr/include/c++/4.6/bits/basic_string.h:2510:5: note: template<class _CharT, class _Traits, class _Alloc> bool std::operator<(const std::basic_string<_CharT, _Traits, _Alloc>&, const std::basic_string<_CharT, _Traits, _Alloc>&)
/usr/include/c++/4.6/bits/basic_string.h:2522:5: note: template<class _CharT, class _Traits, class _Alloc> bool std::operator<(const std::basic_string<_CharT, _Traits, _Alloc>&, const _CharT*)
/usr/include/c++/4.6/bits/basic_string.h:2534:5: note: template<class _CharT, class _Traits, class _Alloc> bool std::operator<(const _CharT*, const std::basic_string<_CharT, _Traits, _Alloc>&)

The other answer explains it nicely. If A defines a weak total ordering (e.g. by implementing operator<) you'll be able to find the vectors.
Here's how to make the code more succinct:
Live On Coliru
#include <iostream>
#include <set>
#include <vector>
struct A {
int a;
bool operator<(A const &other) const { return a < other.a; }
};
int main() {
std::set<std::vector<A> > myset{
{ { 4 }, { 7 }, { 9 } },
{ { 5 }, { 8 }, { 10 } },
{ { 6 }, { 9 }, { 11 } },
};
auto it = myset.find({ {1}, {2}, {3} });
std::cout << "Found vector: " << std::boolalpha << (it != myset.end()) << "\n";
it = myset.find({ {5}, {8}, {10} });
std::cout << "Found vector: " << std::boolalpha << (it != myset.end()) << "\n";
}
Prints
Found vector: false
Found vector: true
This uses aggregate initialization for A and uniform initialization of the vector and set containers ({a,b,c} will use the std::initialializer_list<> constructor overloads)

As it is now, std::set::find is trying to compare the given vector with the vector in the set using std::less<std::vector<A>>, which is the default comparator for instances of std::set<std::vector<A>>. AFAIK, std::less will call bool operator<(const std::vector<A>& lhs, const std::vector<A>& rhs) const to compare the vectors, and this will in turn perform a lexicographical comparison of the vectors by using std::less<A>. std::less<A> will then try to call either bool A::operator<(const A& rhs) const (member function version) or bool operator<(const A& lhs, const A& rhs) const (free function version) to compare instances of A. This fails because neither of these functions exist.
You have two options:
1) You equip your struct A with a comparison operator (in this example, we do it as a member function) like so:
bool operator<(const A& rhs) const { return a < rhs.a; }
and rely on std::vector's implementation of operator<, which performs lexicographical comparison. Lexicographical comparison means that the vector elements are compared pairwise until a pair (x,y) of elements of the two vectors being compared is found where x < y or y < x. If no such pair exists, then both vectors are consider equal, unless they have different lengths, in which case the shorter vector is a prefix of the longer one, and is consider less.
2) You provide a comparator to your std::set that can compare the vectors, specifically, determine which of two vectors is less than the other. This is only useful if you don't want to use std::vector's lexicographical comparison and if you can define a weak total order on your vectors. If that is the case, then you can implement a comparator like so:
struct VecCmp {
bool operator<(const std::vector<A>& lhs, const std::vector<A>& rhs) const {
// determine whether lhs is less than rhs and return true if so, false otherwise
}
};
You then configure your std::set to use this comparator like so:
std::set<std::vector<A>, VecCmp> myset(VecCmp());

Related

Why does libstdc++ implement both <L,R> and <LR> overloads for binary operators on iterators?

In libstdc++3, in the header bits/stl_iterator.h (GCC 10 source here), every binary operator for __normal_iterator has two overloads defined (here is == for example):
template<typename _IteratorL, typename _IteratorR, typename _Container>
_GLIBCXX20_CONSTEXPR
inline bool
operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
const __normal_iterator<_IteratorR, _Container>& __rhs)
_GLIBCXX_NOEXCEPT
{ return __lhs.base() == __rhs.base(); }
template<typename _Iterator, typename _Container>
_GLIBCXX20_CONSTEXPR
inline bool
operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
const __normal_iterator<_Iterator, _Container>& __rhs)
_GLIBCXX_NOEXCEPT
{ return __lhs.base() == __rhs.base(); }
Where .base() returns a pointer to an array element in this case. This is done throughout the library for other iterator types as well. According to comments and changelogs scattered throughout, it's done to support interoperability between iterators and const_iterators.
My question is, why are both the the <_IteratorL, _IteratorR, _Container> and <_Iterator, _Container> overloads defined for all of them? That is, why is <_Iterator, _Container> necessary? Wouldn't the former cover every case? What would break if the latter was removed?
GCC's libstdc++ implementation has a lot of street cred, so I'm sure there's a good, possibly subtle reason, but I can't figure out what it could be.
I ask because I'm currently working out some kinks in my own custom iterator implementations and looking at the STL as a model.
The comment above that complains about std::rel_ops, which provide a template<class T> bool operator!=(const T& lhs, const T& rhs).
Reducing __normal_iterator to it's bare essentials to show the problem, we get this:
#include <utility>
template<typename T>
struct normal_iterator {
T m_base;
const T& base() const { return m_base; }
};
// (1)
template<typename IteratorL, typename IteratorR>
bool operator!=(const normal_iterator<IteratorL>& lhs, const normal_iterator<IteratorR>& rhs) {
return lhs.base() != rhs.base();
}
// (2)
template<typename Iterator>
bool operator!=(const normal_iterator<Iterator>& lhs, const normal_iterator<Iterator>& rhs) {
return lhs.base() != rhs.base();
}
int main() {
using namespace std::rel_ops;
// Your container's `const_iterator` is `const int*`, and `iterator` is `int*`
normal_iterator<const int*> a{nullptr};
normal_iterator<int*> b{nullptr};
a != b; // Uses (1) to compare const_iterator and iterator
a != a; // Uses (2) to compare two iterators
}
Without the second overload, this would not compile since there are two viable functions to call:
std::rel_ops::operator!=<normal_iterator<int*>>(const normal_iterator<int*>&, const normal_iterator<int*>&)
operator!=<int*, int*>(const normal_iterator<int*>&, const normal_iterator<int*>&)
And neither is more specialiased than the other (it is ambiguous)
For std::rel_ops specifically, there is no reason for the extra == overload, but nothing is stopping a user from writing a similar template<typename T> bool operator==(const T&, const T&) in some other namespace.

Overloaded template operator calling separate class operator

I've got a template class containing a priority queue of other classes, I need to use the priority overloader to call the individual class overloaders to compare based on the individual classes preferences (in this case it's age, in another class it could be price.
I've got absolutely no doubt that I've implemented the operator overloading incorrect so would appreciate the advice.
For example
#include <iostream>
#include <queue>
#include <string>
using namespace std;
class Animal {
public:
Animal();
Animal(string t, int a);
int get_age()const;
bool operator< ( Animal& b) const;
void display()const;
private:
string type;
double age;
};
void Animal::display() const
{
cout << "Type: " << type << " Age: " << age;
}
int Animal::get_age() const
{
return age;
}
Animal::Animal(){}
Animal::Animal(string t, int a)
{
type = t;
age = a;
}
bool Animal::operator< ( Animal& b) const
{
return b.get_age();
}
template<typename T>
class Collection {
public:
Collection();
Collection(string n, string d);
void add_item(const T& c);
private:
priority_queue <T> pets;
string name; // Name of the collection
string description; // Descriptions of the collection
};
template<typename T>
Collection<T>::Collection(){}
template<typename T>
Collection<T>::Collection(string n, string d)
{
name = n;
description = d;
}
template<typename T>
bool operator<(const T& one, const T& two)
{
return one.operator<(two);
}
template<typename T>
void Collection<T>::add_item(const T& c)
{
pets.push(c);
}
int main(){
Animal p1("Dog", 10);
Animal p2("Cat", 5);
Animal p3("Turtle", 24);
Collection<Animal> P("Pets", "My Pets");
P.add_item(p1);
P.add_item(p2);
P.add_item(p3);
cout << endl;
return 0;
}
I get this error and I'm not sure what I need to do to fix it. I've got to keep the class overloader as the single variable (Animal& b).
task.cpp: In instantiation of 'bool operator<(const T&, const T&)
[with T = Animal]':
c:\mingw-4.7.1\bin../lib/gcc/mingw32/4.7.1/include/c++/bits/stl_function.h:237:22:
required from 'bool std::less<_Tp>::operator()(const _Tp&, const _Tp&)
const [with _Tp = Animal]'
c:\mingw-4.7.1\bin../lib/gcc/mingw32/4.7.1/include/c++/bits/stl_heap.h:310:4: required from 'void std::__adjust_heap(_RandomAccessIterator,
_Distance, _Distance, _Tp, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator > >; _Distance = int; _Tp = Animal; _Compare =
std::less]'
c:\mingw-4.7.1\bin../lib/gcc/mingw32/4.7.1/include/c++/bits/stl_heap.h:442:4: required from 'void std::make_heap(_RandomAccessIterator,
_RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator > >; _Compare = std::less]'
c:\mingw-4.7.1\bin../lib/gcc/mingw32/4.7.1/include/c++/bits/stl_queue.h:393:9: required from 'std::priority_queue<_Tp, _Sequence,
_Compare>::priority_queue(const _Compare&, const _Sequence&) [with _Tp = Animal; _Sequence = std::vector >; _Compare = std::less]' task.cpp:57:45: required from 'Collection::Collection(std::string, std::string) [with T = Animal;
std::string = std::basic_string]' task.cpp:79:43: required
from here task.cpp:66:30: error: no matching function for call to
'Animal::operator<(const Animal&) const' task.cpp:66:30: note:
candidate is: task.cpp:36:6: note: bool Animal::operator<(Animal&)
const task.cpp:36:6: note: no known conversion for argument 1 from
'const Animal' to 'Animal&' task.cpp: In function 'bool
operator<(const T&, const T&) [with T = Animal]':
Your comparison
bool Animal::operator< ( Animal& b) const
{
return b.get_age(); // returns true always unless age == 0
}
is no comparison and it should take a const parameter. You should have something like
bool Animal::operator< (const Animal& b) const
// ^----------------------- const !
{
return get_age() < b.get_age();
}
Btw you dont need to use a member operator< for the priority queue. Especially if you want to sort objects in different ways I would recommend to not use it, but pass a lambda to the priority_queue. See eg here for an example.
Both of your overloads of < are problematic
bool Animal::operator< ( Animal& b) const
the Animal should also be const. You also need to compare both parameters, otherwise things (such as your priority_queue) that expect < to provide an ordering will have undefined behaviour.
You don't use anything non-public from Animal, so I suggest you change it to
bool operator< (const Animal & lhs, const Animal & rhs)
{ return lhs.get_age() < rhs.get_age(); }
This has the benefit of treating both sides identically, rather than one being implicit.
template<typename T>
bool operator<(const T& one, const T& two)
{
return one.operator<(two);
}
This template matches all types and is entirely superfluous. a < b can call either a member or a free operator <. Just delete this template.

Call super operator== from a vector inherited class in C++

I'm trying to implement the compare operator of a class that inherits from vector.
I want it to compare first its own new attributes and then use the inherited operator from vector. This is an example:
struct A : vector<int> {
int a;
bool operator==(const A& other) {
return a == other.a && vector::operator==(other);
}
}
But I'm getting this error:
no member named 'operator==' in 'std::__1::vector<int, std::__1::allocator<int> >'
Same result with other classes from the STL, but it works well if I inherit from another class of my own.
This is the implementation of vector that I'm using:
inline _LIBCPP_INLINE_VISIBILITY
bool
operator==(const vector<_Tp, _Allocator>& __x, const vector<_Tp, _Allocator>& __y)
{
const typename vector<_Tp, _Allocator>::size_type __sz = __x.size();
return __sz == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());
}
What I'm doing wrong?
vector's equality operator is a non-member function, which means you can't call it like that. You would be better off doing something like:
struct A : std::vector<int> {
int a;
bool operator==(const A& other) {
vector const& self = *this;
return a == other.a && self == other;
}
};
However, I wouldn't recommend inheriting from a standard container. Instead, you should have a std::vector<int> data member (composition over inheritance).

How to sort a std::set with const getters

I have a std::set container whose elements are objects of the following class:
class LaneConnector {
public:
const Lane* getLaneFrom() const {
return From;
}
const Lane* getLaneTo() const {
return To;
}
private:
Lane* From;
Lane* To;
}
and my comparator function is as follows:
struct MyLaneConectorSorter {
bool operator() (LaneConnector * c, LaneConnector * d)
{
Lane* a = const_cast<Lane*>(c->getLaneFrom());
Lane* b = const_cast<Lane*>(d->getLaneFrom());
return (a->getLaneID() < b->getLaneID());
}
} myLaneConnectorSorter;
Now when I try to sort the elements in the set with:
//dont panic, the container just came through a const_iterator of a std::map :)
const std::set<LaneConnector*> & tempLC = (*it_cnn).second;
std::sort(tempLC.begin(), tempLC.end(), myLaneConnectorSorter);
I get a frenzy of errors starting with the following lines, Appreciate if you help me solve this problem.
Thanks:
/usr/include/c++/4.6/bits/stl_algo.h: In function ‘void std::sort(_RAIter, _RAIter, _Compare) [with _RAIter = std::_Rb_tree_const_iterator<LaneConnector*>, _Compare = {anonymous}::MyLaneConectorSorter]’:
/home/.../dev/Basic/shared/conf/simpleconf.cpp:1104:65: instantiated from here
/usr/include/c++/4.6/bits/stl_algo.h:5368:4: error: no match for ‘operator-’ in ‘__last - __first’
/usr/include/c++/4.6/bits/stl_algo.h:5368:4: note: candidates are:
/usr/include/c++/4.6/bits/stl_iterator.h:321:5: note: template<class _Iterator> typename std::reverse_iterator::difference_type std::operator-(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_Iterator>&)
/usr/include/c++/4.6/bits/stl_iterator.h:378:5: note: template<class _IteratorL, class _IteratorR> typename std::reverse_iterator<_IteratorL>::difference_type std::operator-(const std::reverse_iterator<_IteratorL>&, const std::reverse_iterator<_IteratorR>&)
/usr/include/c++/4.6/bits/stl_bvector.h:181:3: note: std::ptrdiff_t std::operator-(const std::_Bit_iterator_base&, const std::_Bit_iterator_base&)
/usr/include/c++/4.6/bits/stl_bvector.h:181:3: note: no known conversion for argument 1 from ‘std::_Rb_tree_const_iterator<LaneConnector*>’ to ‘const std::_Bit_iterator_base&’
First, you cannot sort an std::set. It is a sorted structure, sorting happens upon construction or insertion.
Second, you can construct an std::set with your own sorting functor, and you can avoid unnecessary const_casts by making it take const pointers:
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* lhs, const LaneConnector* rhs) const
{
// you may want to put some null pointer checks in here
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
return a->getLaneID() < b->getLaneID();
}
};
and instantiate the set like this:
std::set<LaneConnector*, MyLaneConectorSorter> s(MyLaneConectorSorter());
or, if you want to construct it from a different set, with a different ordering,
std::set<LaneConnector*> orig = ..... ;
....
std::set<LaneConnector*, MyLaneConectorSorter> s(orig.begin(), orig.end(), MyLaneConectorSorter());

object in a set iterator

I can get a method of a class in a set iterator ?
#include <iostream>
#include <string>
#include <set>
class student{
public:
student(std::string n){
name=n;
}
void print(){
std::cout << name << std::endl;
}
bool operator < (const student & s1){ return true;}
bool operator = (const student & s1){ return true;}
private:
std::string name;
};
int main(){
std::set<student> studs;
studs.insert(student("name01"));
studs.insert(student("name02"));
std::set<student>::iterator it;
for(it = studs.begin(); it != studs.end(); it++)
(*it).print() ;
}
I get this error
students.cpp: In function ‘int main()’:
students.cpp:22: error: passing ‘const student’ as ‘this’ argument of ‘void student::print()’ discards qualifiers
/usr/include/c++/4.2.1/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = student]’:
/usr/include/c++/4.2.1/bits/stl_tree.h:982: 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 = student, _Val = student, _KeyOfValue = std::_Identity<student>, _Compare = std::less<student>, _Alloc = std::allocator<student>]’
/usr/include/c++/4.2.1/bits/stl_set.h:307: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename _Alloc::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(const _Key&) [with _Key = student, _Compare = std::less<student>, _Alloc = std::allocator<student>]’
students.cpp:18: instantiated from here
/usr/include/c++/4.2.1/bits/stl_function.h:227: error: passing ‘const student’ as ‘this’ argument of ‘bool student::operator<(const student&)’ discards qualifiers
with
bool operator<(const student & s1) const { return true;}
bool operator==(const student & s1) const { return true;}
now work!! O_o',
#include <iostream>
#include <string>
#include <set>
class student{
public:
student(std::string n){
name=n;
}
void print() const {
std::cout << name << std::endl;
}
bool operator<(const student & s1) const { return true;}
bool operator==(const student & s1) const { return true;}
private:
std::string name;
};
int main(){
std::set<student> studs;
studs.insert(student("name01"));
studs.insert(student("name02"));
std::set<student>::iterator it;
for(it = studs.begin(); it != studs.end(); it++)
it->print() ;
}
You need to add a const qualifer to your print member function:
void print() const
{
std::cout << name << std::endl;
}
Objects in an std::set are necessarily const, since they are used as keys. When an object (or reference) is constant, you can only call member functions of that object which are declared with the const qualifier.
You also want const qualifiers on both the == and < operator overload functions. (And don't forget to change = to == as pointed out in the comments.)
Yes, though it->print() is more intuitive.
A naive world-view is that iterators are a bit like pointers. There is more to it than that, as explained here.
The most obvious form of iterator is a
pointer: A pointer can point to
elements in an array, and can iterate
through them using the increment
operator (++). But other forms of
iterators exist. For example, each
container type (such as a vector) has
a specific iterator type designed to
iterate through its elements in an
efficient way.
You want operator==, not operator=.
Your operator< definition violates the requirements of std::set, and is inconsistent with your operator<. That is, according to your operator<, nothing is equivalent, but according to your operator==, everything is equal. Operator< should define a irreflexive, transitive, and asymmetric (for non-equivalent values) relation.
Objects in a set are necessarily const, and so to call a function on such an object that function must be declared with the const qualifier. Specifically, print() should be declared void print() const.
Similarly, operator< should be declared with the const qualifier. std::set requires that operator< can be called with const objects. Another valid option would be to make operator< a non-member function and to take both objects by value (bad) or const reference (good).
While not required in your example, operator== should also be declared with the const qualifier.
Write your print() function like this:
void print() const //<---- note this 'const'
{
std::cout << name << std::endl;
}
Now your code should work now. :-)
By the way, such functions with const keyword appearing on the right side, are called const member function, as they cannot change any member-data of the class.
See this FAQ: [18.10] What is a "const member function"?
#include <iostream>
#include <set>
using namespace std;
class Boxer{
public:
string name;
int strength;
};
struct Comp{
bool operator()(const Boxer& a, const Boxer& b){
return a.strength > b.strength;
}
};
int main(){
Boxer boxer[3];
boxer[0].name="uday", boxer[0].strength=23;
boxer[1].name="manoj", boxer[1].strength=33;
boxer[2].name="rajiv", boxer[2].strength=13;
set< Boxer, Comp> s;
s.insert(boxer[0]);
s.insert(boxer[1]);
s.insert(boxer[2]);
set< Boxer, Comp>::iterator it = s.begin();
Boxer b = *it;
cout<<b.name;
//result is Manoj
return 0;
}