C++: find() with custom == operator - c++

I have a case where apparently no operator== is defined for external class and I need to use a find() function. I know I could do this step by step, but I wonder - is there a way to define custom == operator for this find function, similar how we define Hash function for unordered_set? The case:
#include <vector>
#include <algorithm>
#include <MathGeoLib/Math/float3.h>
bool operator==(const math::float3 &lhs, const math::float3 &rhs){
return lhs.x == rhs.x;
}
int main(){
std::vector<math::float3> V;
...
std::find(V.begin(),V.end(),math::float3(0,0,0));
}
returns
binary '==': no operator found which takes a left-hand operand of type math::float3' (or there is no acceptable conversion)
Sometimes I would like to find not exact same vector, but vector close enough - here I would just override operator== with more suitable. Is there any smart way to do that?

You can use std::find_if, here is an example where value_type is double.
The function cmp compares for exact equality, and cmp_epsilon compares for equality within some epsilon.
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
bool cmp(double a, double b)
{
return a == b;
}
bool cmp_epsilon(double e, double a, double b)
{
return a + e >= b and a - e <= b;
}
using namespace std::placeholders;
int main() {
std::vector<double> A = {3./2, 2, 1};
auto i1 = std::find_if(A.begin(),A.end(),std::bind(cmp, 61./40., _1));
std::cout << std::distance(A.begin(), i1) << std::endl;
auto i2 = std::find_if(A.begin(),A.end(),std::bind(cmp_epsilon, 0.1, 61./40., _1));
std::cout << std::distance(A.begin(), i2) << std::endl;
}

For clarity, I would implement a custom find function that accepts a way to compare the elements:
template<class InputIt, class Compare, class T>
InputIt custom_find(InputIt first, InputIt last, Compare comp, const T& value)
{
for (; first != last; ++first) {
if (comp(*first, value)) {
return first;
}
}
return last;
}
You can then give the body of your == operator as a lambda to custom_find:
int main(){
std::vector<math::float3> V;
...
custom_find(V.begin(), V.end(),
[] (const auto& lhs, const auto& rhs) { return your_way_to_compare(lhs,rhs); },
math::float3(0,0,0));
}

Using operator== for anything else than full equality is playing with fire: nobody will expect it, not even you in a couple days/weeks.
Nonetheless, your issue here is likely due to name look-up.
In C++, you should declare free functions in the namespace of one of their arguments. In this case, you should define your operator== in the math namespace.
In short, this is because the compiler starts looking for the right overload in the namespace of the arguments, and stops gathering more overloads to inspect as soon as it has found namespaces that contained some functions... thus it never checks the global namespace.

Related

Is it possible to specify different C++ `operator==` functionality depending on the context at the call site?

I'm trying to make floating point equality comparisons explicit for my custom classes (exact comparison vs approximate comparison). I can avoid overloading the == operator and force users to call functions like exactly_equal or almost_equal instead but this doesn't work with std algorithms out of the box. I was wondering if there was a nice way to get the best of both worlds by forcing some explicit operator lookup at the call site (kind of like std::rel_ops).
For example, let's say I have this CustomType class and some operator== overloads:
struct CustomType {
double value;
};
namespace exactly_equal {
auto operator==(CustomType const& lhs, CustomType const& rhs) -> bool {
return lhs.value == rhs.value;
}
} // namespace exactly_equal
namespace almost_equal {
auto operator==(CustomType const& lhs, CustomType const& rhs) -> bool {
return std::abs(lhs.value - rhs.value) < 1e-2; // This isn't the "right" way, just an example.
}
} // namespace almost_equal
Using this class I can do something like this, which compiles and runs fine:
auto main() -> int {
auto const a = CustomType{1.0/3.0};
auto const b = CustomType{0.3333};
{
using namespace exactly_equal;
if (a == b) {
std::cout << "Exact!" << std::endl;
}
}
{
using namespace almost_equal;
if (a == b) {
std::cout << "Inexact!" << std::endl;
}
}
return 0;
}
The thing that doesn't work is the argument-dependent lookup when using std functions:
auto main() -> int {
auto const items = std::vector<CustomType>{{1.0/3.0}};
auto const value = CustomType{0.3333};
{
using namespace exactly_equal;
// error: no match for 'operator==' (operand types are 'const CustomType' and 'const CustomType')
if (std::find(items.begin(), items.end(), value) != items.end()) {
std::cout << "Exact!" << std::endl;
}
}
{
using namespace almost_equal;
// error: no match for 'operator==' (operand types are 'const CustomType' and 'const CustomType')
if (std::find(items.begin(), items.end(), value) != items.end()) {
std::cout << "Inexact!" << std::endl;
}
}
return 0;
}
Most suggestions for adding operators involve some sort of base class with operator overloads or a pattern similar to using namespace std::rel_ops (which also fails argument-dependent lookup). I am not sure a base class would help for this problem and ideally I would want to use this solution on classes I don't own (and can't modify).
I could use explicit functions and types for my data structures and algorithms:
struct ExactlyEqualPredicate{
auto operator()(CustomType const& lhs, CustomType const& rhs) const -> bool {
return lhs.value == rhs.value;
}
};
struct AlmostEqualComparator{
CustomType value;
auto operator()(CustomType const& other) const -> bool {
return std::abs(value.value == other.value) < 1e-2;
}
};
auto main() -> int {
auto const items = std::vector<CustomType>{{1.0/3.0}};
auto const value = CustomType{0.3333};
if (std::find_if(items.begin(), items.end(), AlmostEqualComparator{value}) != items.end()) {
std::cout << "Inexact!" << std::endl;
}
auto custom_map = std::unordered_map<CustomType,
std::string,
std::hash<CustomType>,
ExactlyEqualPredicate>{
{CustomType{0.3333}, "Exact!"},
};
if (auto iter = custom_map.find(value); iter != custom_map.end()) {
std::cout << iter->second << std::endl;
}
return 0;
}
but this becomes quite verbose and breaks down when I have nested containers (std::vector<std::vector<CustomType>>) or other complex structures. Template classes seem to have a couple extra rules for argument-dependent lookup but a nice way to accomplish this with templates was not immediately clear to me.
The goal is to keep the equality comparison explicit (exact comparison vs approximate comparison) at the call site without making usage of external functions and std algorithms overly difficult and/or verbose. Is there a solution I'm missing or some black magic I haven't thought of that might make this possible? I currently use C++17 in my codebase.
Thanks!
You have already enumerated the plausible, flawed approaches with the exception of wrapper types that define their own comparisons. They might store pointers or references to the underlying objects to avoid copying, although even that would not allow algorithms to interpret containers differently. For that, the modern solution is to use Ranges (perhaps as included in C++20) with its composable transformations and projections (much like in your last approach).

Generic comparison operator for structs

In many of my unit tests I need to compare the contents of simple structs having only data members:
struct Object {
int start;
int stop;
std::string message;
}
Now, if I want to write something like:
CHECK(object1==object2);
I always have to implement:
bool operator==(const Object& lhs, const Object& rhs) {
return lhs.start==rhs.start && lhs.stop==rhs.stop && lhs.message=rhs.message;
}
Writing all these comparison functions becomes tedious, but is also prone to errors. Just imagine, what will happen if I add a new data member to Object, but the comparison operator will not be updated.
Then I remembered my knowledge in Haskell and the magic deriving(Eq) directive, which just generates a sane comparison function for free.
How, could I derive something similar in C++?
Happily, I figured out that C++17 comes with a generic operator== and that every struct should be easily convertible to an std::tuple by the virtue of std::make_tuple.
So I boldly tried the following:
#include <tuple>
#include <iostream>
#include <tuple>
template<typename T>
bool operator==(const T& lhs, const T& rhs)
{
auto leftTuple = std::make_tuple(lhs);
auto rightTuple = std::make_tuple(rhs);
return leftTuple==rightTuple;
}
struct Object
{
std::string s;
int i;
double d;
};
int main(int arg, char** args)
{
std::cout << (Object{ "H",1,2. } == Object{ "H",1,2. }) << std::endl;
std::cout << (Object{ "A",2,3. } == Object{ "H",1,2. }) << std::endl;
return EXIT_SUCCESS;
}
But, unfortunately it just doesn't compile and I really don't know why. Clang tells me:
main.cpp:11:18: error: use of overloaded operator '==' is ambiguous (with operand types
'std::tuple<Object>' and 'std::tuple<Object>')
return leftTuple==rightTuple;
Can I possibly fix this compile error to get my desired behavior?
No, since comparing tuples reverts to comparing the elements of the tuple, so leftTuple == rightTuple tries to compare two Objects which is not possible.
that every struct should be easily convertible to an std::tuple by the virtue of std::make_tuple
No, you'll just get a tuple with one element, the struct.
The trick is to use std::tie:
std::tie(lhs.mem1, lhs.mem2) == std::tie(rhs.mem1, rhs.mem2)
but that has the same problem as your original solution. Unfortunately C++17 doesn't have any facility to avoid this problemyou could write a macro :). But in C++20 you will be able to do:
struct Object
{
std::string s;
int i;
double d;
bool operator==(const Object &) const = default;
};
which will generate the correct comparison operators for Object.

Sorting just two elements using STL

Quite often I have two variables foo1 and foo2 which are numeric types. They represent the bounds of something.
A user supplies values for them, but like a recalcitrant musician, not necessarily in the correct order!
So my code is littered with code like
if (foo2 < foo1){
std::swap(foo2, foo1);
}
Of course, this is an idiomatic sort with two elements not necessarily contiguous in memory. Which makes me wonder: is there a STL one-liner for this?
I suggest to take a step back and let the type system do the job for you: introduce a type like Bounds (or Interval) which takes care of the issue. Something like
template <typename T>
class Interval {
public:
Interval( T start, T end ) : m_start( start ), m_end( end ) {
if ( m_start > m_end ) {
std::swap( m_start, m_end );
}
}
const T &start() const { return m_start; }
const T &end() const { return m_end; }
private:
T m_start, m_end;
};
This not only centralizes the swap-to-sort code, it also helps asserting the correct order very early on so that you don't pass around two elements all the time, which means that you don't even need to check the order so often in the first place.
An alternative approach to avoid the issue is to express the boundaries as a pair of 'start value' and 'length' where the 'length' is an unsigned value.
No, but when you notice you wrote the same code twice it's time to write a function for it:
template<typename T, typename P = std::less<T>>
void swap_if(T& a, T& b, P p = P()) {
if (p(a, b)) {
using std::swap;
swap(a, b);
}
}
 
std::minmax returns pair of smallest and largest element. Which you can use with std::tie.
#include <algorithm>
#include <tuple>
#include <iostream>
int main()
{
int a = 7;
int b = 5;
std::tie(a, b) = std::minmax({a,b});
std::cout << a << " " << b; // output: 5 7
}
Note that this isn't the same as the if(a < b) std::swap(a,b); version. For example this doesn't work with move-only elements.
if the data type of your value that you're going to compare is not already in c++. You need to overload the comparison operators.
For example, if you want to compare foo1 and foo2
template <class T>
class Foo {
private:
int value; // value
public:
int GetValue() const {
return value;
}
};
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.GetValue() < rhs.GetValue());
}
If your value is some type of int, or double. Then you can use the std::list<>::sort member function.
For example:
std::list<int> integer_list;
int_list.push_back(1);
int_list.push_back(8);
int_list.push_back(9);
int_list.push_back(7);
int_list.sort();
for(std::list<int>::iterator list_iter = int_list.begin(); list_iter != int_list.end(); list_iter++)
{
std::cout<<*list_iter<<endl;
}

Is it possible to use std::sort with a sort function that takes extra arguments?

This is something that I've been considering for a while. I've done some research and can't find anything on it, but I haven't found anything to the contrary either.
Consider the std::sort function in <algorithm>. It takes two iterators and a function pointer as arguments. So if I wanted to sort a vector of strings alphabetically, I would do something like this:
bool ascending(std::string lhs, std::string rhs) { return lhs < rhs; }
std::sort(my_vector.begin(), my_vector.end(), ascending);
The thing is that this type of sort function is case-sensitive, so would place a string beginning with lowercase 'a' after strings beginning with uppercase 'Z'. The only visible solution I see to this is creating an additional function along the lines of bool ascending_case_insensitive(). However, it would be nice if I could have a function bool ascending() with an additional bool is_case_sensitive parameter to use in sort. Is this possible?
Where you now have
bool ascending(std::string lhs, std::string rhs);
std::sort(my_vector.begin(), my_vector.end(), ascending);
you can have
bool ascending(std::string lhs, std::string rhs, bool case_sensitive);
using namespace std::placeholders;
std::sort(my_vector.begin(), my_vector.end(), std::bind(ascending, _1, _2, false));
The point of std::bind is to return an object that when invoked, calls the bound function, optionally with altered arguments. You can use it to change argument order, add optional parameters, or set parameters to specific fixed values.
Since std::sort takes an instance of the comparison functor, you can use arguments to your functor's constructor determine its behaviour. For example,
class StringCompare
{
public:
StringCompare(bool is_case_sensitive=true) : is_case_sensitive(is_case_sensitive){}
bool operator()(const string&, const string&);///This would handle the comparison using the is_case_sensitive flag
private:
bool is_case_sensitive;
};
std::sort(my_vector.begin(), my_vector.end(), StringCompare(true));//case-sensitive comparison
std::sort(my_vector.begin(), my_vector.end(), StringCompare(false));//case-insensitive comparison
There follows an example that includes a function call with a bound extra parameter and a lambda expression that captures the extra parameter by value:
#include <iostream>// for std::cout
#include <vector>// for std::vector
#include <functional> // for std::bind
#include <algorithm> // for std::sort
bool ltMod(int i, int j, int iMod) {
return (i % iMod) < (j % iMod);
}
int main() {
std::vector<int> v = {3,2,5,1,4};
int iMod = 4;
std::cout << "\nExample for the usage of std::bind: ";
// _1 and _2 stand for the two arguments of the relation iMod is the bound parameter
std::sort(v.begin(),v.end(),std::bind(ltMod,std::placeholders::_1,std::placeholders::_2,iMod));
for( auto i : v ) std::cout << i << ',';
iMod = 3;
std::cout << "\nExample for lambda: ";
// lambdas are unnamed inplace functions
// iMod is captured by value. You can use the value within the function.
std::sort(v.begin(),v.end(),[iMod](int i, int j){ return ltMod(i,j,iMod); });
for( auto i : v ) std::cout << i << ',';
return 0;
}
/**
Local Variables:
compile-command: "g++ -std=c++11 test.cc -o a.exe"
End:
*/
Thought that I would answer my own question in order to summarize the responses I've gotten. So from what I gather, I basically have two options.
The first would be to write a lambda function to handle my one-time case.
// Lambda solution.
std::sort(my_vector.begin(), my_vector.end(),
[](std::string const &lhs, std::string const &rhs) // Thanks for optimizing my example code guys. No, seriously. ;)
{
return boost::toupper(lhs) < boost::toupper(rhs);
});
The second, more reusable option would be to create a functor to handle sort situations like these.
// Functor solution.
class SortAscending
{
private:
bool _is_case_sensitive;
public:
SortAscending(bool is_case_sensitive) :
_is_case_sensitive(is_case_sensitive);
bool operator()(std::string const &lhs, std::string const &rhs)
{
if (_is_case_sensitive)
return boost::toupper(lhs) < boost::toupper(rhs);
else
return lhs < rhs;
}
};
std::sort(my_vector.begin(), my_vector.end(), SortAscending(false));
So think that pretty much sums up my options?

How to create a set with my customized comparison in c++

Could someone explain me what is going on in this example here?
They declare the following:
bool fncomp (int lhs, int rhs) {return lhs<rhs;}
And then use as:
bool(*fn_pt)(int,int) = fncomp;
std::set<int,bool(*)(int,int)> sixth (fn_pt)
While the example for the sort method in algorithm library here
can do like this:
bool myfunction (int i,int j) { return (i<j); }
std::sort (myvector.begin()+4, myvector.end(), myfunction);
I also didn't understand the following:
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
I was trying to make a set of C-style string as follows:
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
set <wrap, compare> myset;
I thought I could create a set defining my sorting function in a similar as when I call sort from algorithm library... once it didn't compile I went to the documentation and saw this syntax that got me confused... Do I need to declare a pointer to a function as in the first example i pasted here?
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
Defines a functor by overloading the function call operator. To use a function you can do:
int main() {
std::set <wrap, bool (*)(wrap,wrap)> myset(compare);
return 0;
}
Another alternative is to define the operator as a part of the wrap class:
struct wrap {
char grid[7];
bool operator<(const wrap& rhs) const {
return strcmp(this->grid, rhs.grid) == -1;
}
};
int main() {
wrap a;
std::set <wrap> myset;
myset.insert(a);
return 0;
}
You're almost there... here's a "fixed" version of your code (see it run here at ideone.com):
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2) // more efficient: ...(const wrap& e1, const wrap# w2)
{
return strcmp(w1.grid, w2.grid) < 0;
}
set <wrap, bool(*)(wrap, wrap)> myset(compare);
int main() {
wrap w1 { "abcdef" };
wrap w2 { "ABCDEF" };
myset.insert(w1);
myset.insert(w2);
std::cout << myset.begin()->grid[0] << '\n';
}
"explain [to] me what is going on in this example"
Well, the crucial line is...
std::set<wrap, bool(*)(wrap, wrap)> myset(compare);
...which uses the second template parameter to specify the type of function that will perform comparisons, then uses the constructor argument to specify the function. The set object will store a pointer to the function, and invoke it when it needs to compare elements.
"the example for the sort method in algorithm library..."
std::sort in algorithm is great for e.g. vectors, which aren't automatically sorted as elements are inserted but can be sorted at any time. std::set though needs to maintain sorted order constantly, as the logic for inserting new elements, finding and erasing existing ones etc. all assumes the existing elements are always sorted. Consequently, you can't apply std::sort() to an existing std::set.
"this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
operator()(...) can be invoked on the object using the same notation used to call a function, e.g.:
classcomp my_classcomp;
if (my_classcomp(my_int1, my_int_2))
std::cout << "<\n";
As you can see, my_classcomp is "called" as if it were a function. The const modifier means that the code above works even if my_classcomp is defined as a const classcomp, because the comparison function does not need to modify any member variables of the classcomp object (if there were any data members).
You almost answered your question:
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
struct wrap_comparer
{
bool operator()(const wrap& _Left, const wrap& _Right) const
{
return strcmp(_Left.grid, _Right.grid) == -1;
}
};
// declares pointer to function
bool(*fn_pt)(wrap,wrap) = compare;
// uses constructor with function pointer argument
std::set<wrap,bool(*)(wrap,wrap)> new_set(fn_pt);
// uses the function directly
std::set<wrap,bool(*)(wrap,wrap)> new_set2(compare);
// uses comparer
std::set<wrap, wrap_comparer> new_set3;
std::sort can use either a function pointer or a function object (http://www.cplusplus.com/reference/algorithm/sort/), as well as std::set constructor.
const modifier after function signature means that function can't modify object state and so can be called on a const object.