binary_search with std::pair using a custom operator - c++

I am trying to do a binary_search including a vector of integer pairs and an integer as follows:
#include <vector>
#include <algorithm>
using namespace std;
typedef vector<pair<size_t,size_t> > int_pairs;
bool operator<(const size_t& l, const pair<size_t,size_t>& r)
{return r.first < l;} // useful for binary search
int main(){
int_pairs pairs_vec;
pairs_vec.push_back(pair <size_t,size_t>(1,2));
pairs_vec.push_back(pair <size_t,size_t>(2,2));
size_t i(2);
binary_search(pairs_vec.begin(),pairs_vec.end(),i);
}
The compiler tells me that the operator< is not defined:
erreur: no match for ‘operator<’ (operand types are ‘const long unsigned int’ and ‘std::pair<long unsigned int, long unsigned int>’)
Am I doing it the right way? I tried to change the definition of the operator in many different ways, but nothing seems to work.

The reason this doesn't work is that operator< isn't looked up from the point you call binary_search, but rather later from inside its body - and that's inside namespace std.
Since std::pair already defines relational operators in namespace std, these hide your overload in global scope and it's never found by name lookup.
The solution is to not use operator< at all. Create your own comparator class that doesn't clash with anything, overload its operator(), and use the other overload of binary_search that lets you specify custom comparator. Something like this:
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<size_t, size_t> my_pair;
typedef vector<pair<size_t,size_t> > int_pairs;
struct Comp {
// important: we need two overloads, because the comparison
// needs to be done both ways to check for equality
bool operator()(my_pair p, size_t s) const
{ return p.first < s; }
bool operator()(size_t s, my_pair p) const
{ return s < p.first; }
};
int main(){
int_pairs pairs_vec;
pairs_vec.push_back(pair <size_t,size_t>(1,2));
pairs_vec.push_back(pair <size_t,size_t>(2,2));
size_t i(2);
binary_search(pairs_vec.begin(),pairs_vec.end(),i, Comp());
}
Side notes:
Your attempt at operator< was wrong, because you switched the order of operands inside the function. The contract for comparators for strict weak ordering says that it must return true if first operand comes before the second (this goes for all comparison functions throughout the standard library).
bool operator<(const size_t& l, const pair<size_t,size_t>& r)
{
return r.first < l; // Wrong!
}
And as said above in the comment, if the operands for the comparison are of different types, you'll need two overloads. To check for equality with <, you need two test:
(!(a < b) && (!b < a))

Related

Concepts-Lite iterator comparison compares values

I've been racking my brain on this one for a few months on and off. I have defined a set of concepts which loosely describe containers and predicates.
In general, I'm looping over the container using an old-fashioned for loop and comparing equality of a parameter iterator to the end(container) iterator. What I can't figure out is why this won't compile when given a Container<T> such that T does not have an equality comparer (T::operator==(T other))
These concepts are by no means complete, and are simply for learning purposes, there are no doubt other ways to accomplish what I'm doing.
The problem code is in this short function defined in "queryalgorithms.h":
#pragma once
#include <algorithm>
#include "concepts.h"
template<typename C, typename P, typename V = typename C::value_type, typename I = typename C::iterator>
I last(C & collection, I iterator, P predicate) requires Container<C> && Predicate<P, V> && Incrementable<I> {
I out;
for (iterator; iterator != end(collection); iterator++) {
}
return out;
}
The test code which fails is here:
#include <iostream>
#include <vector>
#include <algorithm>
#include <forward_list>
#include "queryalgorithms.h"
using namespace std;
class MyClass {
public:
int value;
MyClass(int v): value(v) {};
};
template <typename T>
void Test() {
auto collection = T();
for (auto i = 0; i < 10; i++)
last(collection, begin(collection), [] (auto x) { return true; });
}
int main(int argc, char * argv[]) {
Test<vector<MyClass>>();
return 0;
}
Not sure if this is much help, but for completeness the concepts.h file is here. It's a lot and I don't feel it adds much value so I put that on pastebin.
As described above the small example only compiles for types which have an equality comparer defined (e.g. Test<vector<int>>() will work fine). And the compiler error from g++ is as follows:
/usr/include/c++/6.3.1/bits/stl_algobase.h:800:22: error: no match
for ‘operator==’ (operand types are ‘const MyClass’ and ‘const MyClass’)
if (!(*__first1 == *__first2))
So my question is, why is the compiler deducing the types of iterator != end(collection) to be the value type V rather than the iterator type I?
Edit: I'm compiling using g++ -fconcepts --std=c++17 -o test test.cc
Container<T> requires EqualityComparable<T> which requires a == b for lvalues a and b of type T. The C++ standard says that a == b for a container requires its value type to be EqualityComparable (N4659 [container.requirements.general] Table 83).
Checking Container<vector<MyClass>> in the requires clause on last instantiates vector's == operator, which is most likely implemented in terms of std::equal, which I speculate is the algorithm being defined on line 800 of stl_algobase.h.
Since MyClass is not EqualityComparable, vector<MyClass> does not model Container. The standard notably does not require vector's (or any container's) operator== to be constrained, so you get a hard error checking the constraints instead of last SFINAE-ing away and removing itself from overload resolution.

Why "no match for 'operator<'" when I declared it?

It works for the struct xy that I declared. Why doesn't the same pattern work for complex<int>?
#include <complex>
#include <set>
using namespace std;
struct xy {
int x, y;
};
bool operator< (const xy &a, const xy &b) {
return a.x < b.x;
}
bool operator< (const complex<int> &a, const complex<int> &b) {
return a.real() < b.real();
}
int main() {
xy q;
set<xy> s;
s.insert(q);
complex<int> p;
set< complex<int> > t; //If I comment out these two lines,
t.insert(p); //it compiles fine.
return 0;
}
The error message:
In file included from c:\m\lib\gcc\mingw32\4.8.1\include\c++\string:48:0,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\locale_classes.h:40,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\ios_base.h:41,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\ios:42,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\istream:38,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\sstream:38,
from c:\m\lib\gcc\mingw32\4.8.1\include\c++\complex:45,
from test.cpp:1:
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h: In instantiation of 'bool less<>::operator()(const _Tp&, const _Tp&) const':
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1321:11: required from 'pair<> _Rb_tree<>::_M_get_insert_unique_pos(const key_type&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1374:47: required from 'pair<> _Rb_tree<>::_M_insert_unique(_Arg&&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_set.h:463:29: required from 'pair<> __cxx1998::set<>::insert(const value_type&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\debug\set.h:220:59: required from 'pair<> __debug::set<>::insert(const value_type&)'
test.cpp:28:19: required from here
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h:235:20:
error: no match for 'operator<' (operand types are 'const std::complex<int>' and 'const std::complex<int>')
{ return __x < __y; }
My best guess is that this has something to do with complex<T> being a class, not a struct. By I can't see the logic of why that should make a difference. Or is it some template horribleness?
What I see happening is that the STL at some point tries (roughly speaking) to do a < b, where a and b are complex<int> instances. So it's looking for bool operator< (const complex<int> &a, const complex<int> &b). Well, there is exactly that declared just above main(). Why is it being unreasonable? I thought maybe it didn't like them being references. But removing the ampersands made no difference to its complaint.
One option is to write a custom comparison functor, and instantiate the set with this.
#include <complex>
#include <set>
bool operator< (const std::complex<int> &a, const std::complex<int> &b) {
return a.real() < b.real();
}
struct my_less {
bool operator() (const std::complex<int>& lhs, const std::complex<int>& rhs) const{
return lhs < rhs;
}
};
int main() {
std::complex<int> p;
std::set< std::complex<int>, my_less > t;
t.insert(p);
return 0;
}
Sample on Coliru
It works for the struct xy that I declared. Why doesn't the same pattern work for complex<int>?
The reason is that when you use types from namespace std only, like std::set and std::complex, the compiler has no reason to look for operators in any other namespaces.
With struct xy this is different, as the operator is declared together with the type.
And also, the current standard says:
The effect of instantiating the template complex for any type other than float, double, or long double is
unspecified.
So using complex<int> might, or might not, work depending on which compiler you use. "Unspecified" is not as bad as "undefined", but
of course not very reliable or portable.
As of today, I am facing this issue with my Ubuntu's g++. Suppose I have:
namespace X { namespace Y { class C { ... }; } }
Now operator== is recognized if it's defined in a global namespace, such as:
bool operator== (const X::Y::C& lhs, const X::Y::C& rhs) { return lhs == rhs; }
However, somehow compiler doesn't recognize operator<, if defined in the same way.
Now following way works well:
namespace X { namespace Y {
bool operator< (const C& lhs, const C& rhs) { return lhs < rhs; } } }
However, whether you should do it the same with expanding standard namespace std is a controversial topic. :-)

specialize std::greater via std::rel_ops

How I can specialize std::greater by using std::rel_ops?
I have something like this
#include <utility>
#include <functional>
using namespace std::rel_ops;
struct MyStruct {
int field;
bool operator < (const MyStruct& rhs) const {
return field < rhs.field;
}
};
So I need to sort elements in descending order. How I can do it by using operator <, std::rel_ops and std::greater?
I'm assuming you tried to do something similar to
MyStruct ms[] = {{10}, {50}, {30}, {20}};
std::sort(std::begin(ms), std::end(ms), std::greater<MyStruct>{});
This fails to compile because no suitable operator> will be found. That's because std::greater relies upon ADL to find the operator overload, and ADL searches in associated namespaces. std::rel_ops is not an associated namespace for MyStruct. You can get everything to work by adding a using declaration to the same namespace as MyStruct so that the relevant operator> will be found.
using std::rel_ops::operator>;
Live demo
But this is ugly, and not a viable solution in general, so forget about std::rel_ops and use Boost.Operators as Barry suggests.
You'd have to do it this way:
std::vector<MyStruct> v{...};
std::sort(v.begin(), v.end(), [](const MyStruct& lhs, const MyStruct& rhs){
using namespace std::rel_ops;
return lhs > rhs;
});
Although std::rel_ops is pretty lame. It's easier to use boost::less_than_comparable, in which you just add the operators directly into MyStruct:
struct MyStruct
: boost::less_than_comparable<MyStruct> // <== adds operator>,
// operator>=,
// and operator<=
{
MyStruct(int i) : field(i) { }
int field;
bool operator<(const MyStruct& rhs) const {
return field < rhs.field;
}
};
And then you can sort it the obvious way:
std::sort(v.begin(), v.end(), std::greater<MyStruct>());
std::rel_ops generate the other comparisons from == and < so you first need to define at least <
bool operator<(const MyStruct & lhs, const MyStruct & rhs) {
return lhs.field < rhs.field;
}
Now rel_ops generate > so now you can use std::greater in std::sort
std::sort(begin(myVector), end(myVector), std::greater<MyStruct>());

Conditionally overload an operator

I'm currently working on implementing some mathematical base operations and try to avoid using third party libraries as much as possible. I'm stuck at overloading the operator* for the multiplication of a Scalar*Vector and Vector*Scalar. The current code for the dot product of Scalar*Vector:
#include <vector>
#include <type_traits>
template<class Vector, class Scalar>
typename std::enable_if<std::is_floating_point<Scalar>::value, Vector>::type operator*
(
const Scalar &a,
const Vector &b
)
{
return Vector
(
a*b[0],
a*b[1],
a*b[2]
);
}
int main()
{
const std::vector<double> v1({1,2,3});
const double s1(2);
const auto result(s1*v1);
std::cout<< result << std::endl;
}
The compiler error message is:
error: invalid operands to binary expression ('double' and 'const std::vector')
Any advises on how to overload the * operator, so that both dot-products are possible? I do not intend to implement these two operators in a custom vector class, as overloaded operators. Rather than that, I aim for the templated operator.
I've found a great explanation here and used it to adjust it for the vector operations. In the vector class header file, you basically define via the struct is_vector that anything of type T is not a vector by default. In the following a all types that can act as a vector have to be listed explicitely, such as std::vector.
#include <vector>
#include <type_traits>
template <typename T>
struct is_vector
{
static const bool value = false;
};
template <>
struct is_vector< std::vector >
{
static const bool value = true;
};
template <class Vector>
typename std::enable_if<std::is_vector<Vector>::value, double>::type
operator*(const Vector &a, const Vector &b);
And in the executable, the code looks the same.
int main()
{
const std::vector<double> v1({1,2,3});
const double s1(2);
const auto result(s1*v1);
std::cout<< result << std::endl;
}

Do I need a full ordering on class K to use std::map<K, L> [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What requirements must std::map key classes meet to be valid keys?
I want to use std::map as map from my class to another one. If I try the following code, I get an error "undefined operator <". Does it mean that I need an ordering on class K to use map? And does it have to be full ordering? And do I need all four ordering operators or > is enough?
#include <iostream>
#include <map>
#include <stdio.h>
using namespace std;
struct K {
int i, j;
K(int i, int j) : i(i), j(j){}
friend bool operator==(const K& x, const K& y){ return (x.i==y.i)&&(x.j==y.j); }
friend bool operator!=(const K& x, const K& y){ return !(x==y); }
/* friend bool operator<(const K&x, const K&y){
if(x.i<y.i) return true;
if(x.i>y.i) return false;
return x.j<y.j;
}
friend bool operator>(const K&x, const K&y){ return y<x; }
friend bool operator<=(const K&x, const K&y){ return !(y<x); }
friend bool operator>=(const K&x, const K&y){ return !(x<y); }
*/
};
int main(){
map<K, float> m;
m[K(1,2)]=5.4;
if(m.find(K(1,2))!=m.end())
cout << "Found: " << m[K(1,2)] << endl;
else
cout << "Not found" << endl;
return 0;
}
Yes, you need a way to compare elements (operator<) in order to use the std::map. One of the features of map is that it keeps its contents in sorted order, but to achieve this its need to know how to compare items.
You have three options to implement a comparison method:
Add operator< definition in K
Make a comp functor that know to how to compare two K elements and add this as a template parametermap<K, float, comp> m;
struct comp {
bool operator()(const K& first, const K& second) {
/*****/
}
};
You can define the std::less specialization for K
template<> struct less<K>
{
bool operator()(const K& first, const K& second) {
/*****/
}
};
And simple use map<K, float> m;
This works because by the template definition for map has the compare function set to std::less.
template < class Key, class T, class Compare = less,
class Allocator = allocator > > class map
The elements in your map are referenced by a comparison function on the Key type you supply.
Either implicitly as std::less or explicitly as third template argument.
If you use a custom key type, you also need to supply an appropriate comparison function (or functional object) that imposes a strict weak ordering on the keys.
That is, if the keys appear equal
!(key1 < key2 || key2 < key1)
the items are considered equivalent.
Thus, if your comparison function only provides a partial order on the keys, elements may be considered equal that actually are different, and thereby their values might interfere with each other.
Just define operator<
Everything else is unnecessary for the purpose of std::map ordering.
An std::map needs simply an operator<. Implementations usually employ a "red-black" tree which can be built to only requires a < operator.
However you can use std::unordered_map exactly how you just did. It typically uses a generic hash function; you are free to supply your own hash function for it if suits your problem-space since C++11.