How does stl containers' `less` parameter works? - c++

Let take map for this example.
I set the map object: map<const char*, int, compare> a, as compare is the following:
struct compare : public std::binary_function<const char*, const char*, bool>
{
bool operator() (const char* a, const char* b) {
return strcmp(a, b) < 0;
}
};
What have I done here? How did I overload this operator? Isn't that unary operator?
It's working, but I'm not sure that I really know what I've written here.
this is the complete code:
#include <set>
#include <map>
#include <string>
#include <iostream>
using namespace std;
struct compare : public std::binary_function<const char*, const char*, bool>
{
bool operator() (const char* a, const char* b) {
return strcmp(a, b) < 0;
}
};
int main() {
map<const char*, int, compare> a;
a["Mike"] = 5;
a["Tre"] = 3;
a["Billie"] = 20;
for(map<const char*, int, compare>::iterator it = a.begin(); it != a.end(); ++it) {
cout << (*it).first << endl;
}
cin.get();
}

Your compare definition permits the following:
compare cmp;
bool result = cmp("foo", "bar"); // Two arguments, therefore not unary!
Thus std::map can use it to determine the relative ordering of pairs of elements. This is required in order to construct a binary search tree behind-the-scenes.

What have I done here?
You created a function object - an object that provides an implementation for the operator (), and supplied its class to instantiate the std::map template.
How did I overload this operator?
You provided its public implementation (note that struct makes its members public by default).
Isn't that unary operator?
No. A unary operator takes one operand; your operator takes two operands, and is, therefore, binary.

Related

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

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.

how to trivially adapt set or map ordering predicate for pointers

There must be a trivial answer to this...
I have a std::set or a std::map or some object type which has a natural ordering - say std::less.
I need to change my set or map to contain shared_ptr instead of copies of T.
So I want something like:
using my_set std::set<std::shared_ptr<T>, std::less<*T>>;
But I'm drawing a blank as to how to specify "use the less adaptor on ____ adaptor of T so that it's on dereferenced members, not on shared_ptrs!"
Is there a std::less<std::dereference<std::shared_ptr<T>>> equivalent?
There is currently no functor in the C++ standard library to achieve what you want. You can either write a custom comparator, or if you need this functionality often, come up with an indirect/dereference function object.
Related and potentially helpful threads; the first one offers a generic solution for many operators (even if it requires a bit of code):
Why do several of the standard operators not have standard functors?
Functor that calls a function after dereferencing?
Less-than function dereferencing pointers
While the standard library may not already provide what you need, I think it's pretty trivial to write your own std::dereference_less:
#include <memory>
#include <set>
namespace std
{
template<typename T>
struct dereference_less
{
constexpr bool operator ()(const T& _lhs, const T& _rhs) const
{
return *_lhs < *_rhs;
}
};
}
int main()
{
using key_type = std::shared_ptr<int>;
std::set<key_type, std::dereference_less<key_type>> mySet;
}
Demo (refactored a bit to have a template type alias like in your question)
Since you are already changing your internal interface to something that requires dereferencing you could also just write a wrapper class and provide a bool operator< () as follows:
#include <memory> // shared_ptr
#include <set> // set
#include <iostream> // cout
using namespace std;
template<typename T>
class wrapper
{
public:
shared_ptr<T> sp;
bool operator< (const wrapper<T>& rhs) const
{
return *( sp.get() ) < *( rhs.sp.get() ) ;
}
wrapper(){}
wrapper(shared_ptr<T> sp):sp(sp){}
};
int main()
{
shared_ptr<int> sp1 (new int);
*sp1 = 1;
shared_ptr<int> sp2 (new int);
*sp2 = 2;
set<wrapper<int>> S;
S.insert(wrapper<int>(sp2));
S.insert(wrapper<int>(sp1));
for (auto& j : S)
cout << *(j.sp) << endl;
return 0;
}

Handling constness of pointed values in map keys

I have the following code:
#include <map>
using namespace std;
struct A {};
map</*const*/ A *, int> data;
int get_attached_value(const A *p) {
return data.at(p);
}
void reset_all() {
for (const auto &p : data) *p.first = A();
}
My problem is that this code fails on a type error both when I comment and uncomment the const in the type of data. Is there any way I can solve this without using const_cast and without losing the const in get_attached_value?
The problem seems to be in the pointee type, which has to be the same in both pointer declarations (map key type and the get_attached_value's argument).
OP's code uses const A*, which is a pointer to a const instance of class A (an alternative spelling is A const *). Leaving this const in both map declaration and in get_attached_value' argument almost works, but reset_all does not allow you to assign a new value to *p.first, because the resulting type is A const& (which cannot be assigned into).
Removing both consts works as well, but OP wants to keep a const in get_attached_value.
One solution for OP's requirements, keeping as many consts as possible, seems to be to change the pointer type to a const pointer to a non-const instance of A. This will keep reset_all working, while allowing to use a const pointer in both map declaration and get_attached_value's argument:
#include <map>
using namespace std;
struct A {};
map<A * const, int> data;
int get_attached_value(A * const p) {
return data.at(p);
}
void reset_all() {
for (const auto &p : data)
*p.first = A();
}
Another possible solution, with map's key as non-const but the get_attached_value's parameter const, could use std::lower_bound with a custom comparator to replace the data.at() call:
#include <map>
#include <algorithm>
using namespace std;
struct A {};
map<A*, int> data;
int get_attached_value(A const * const p) {
auto it = std::lower_bound(data.begin(), data.end(), p,
[] (const std::pair<A* const, int>& a, A const* const b) {
return a.first < b;
}
);
return it->second;
}
void reset_all() {
for (const auto &p : data)
*p.first = A();
}
However, this solution will be significantly less efficient than one that would use map's native search functions - std::lower_bound uses linear search when input iterators are not random access.
To conclude, the most efficient solution in C++11 or lower would probably use a const pointer as the map's key, and a const_cast in the reset_all function.
A bit more reading about const notation and pointers can be found here.

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.

subscript operators for class with std::map member variable

I am trying to make a class that wraps std::map and does checking to make sure the keys are one the of approved valid strings, and also initializes the map to have default values for all the approved valid strings. I am having issues getting the subscript operator to work, specifically the const version of it.
Here is my class prototyping code:
#include <set>
#include <string>
#include <map>
class foo {
public:
foo() {}
const double & operator[](const std::string key) const {
return data[key];
}
private:
static const std::set<std::string> validkeys;
std::map<std::string, double> data;
};
const std::set<std::string> foo::validkeys = {"foo1", "foo2"};
When I compile this (using g++ with -std=c++0x), I get this compilation error:
|| /home/luke/tmp/testmap.cc: In member function 'double& foo::operator[](std::string) const':
testmap.cc|10 col 22 error| passing 'const std::map<std::basic_string<char>, double>' as
'this' argument of 'mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const
key_type&) [with _Key = std::basic_string<char>, _Tp = double, _Compare =
std::less<std::basic_string<char> >, _Alloc = std::allocator<std::pair<const
std::basic_string<char>, double> >, mapped_type = double, key_type =
std::basic_string<char>]' discards qualifiers
Nothing I do seems to fix this. I have tried
making validkeys a std::set and data std::map
using const char * instead of string
returning const double or double instead of const double &
using list and vector instead of set to store the validkeys
I don't know if I'm even approaching this problem correctly so if there is some other simple way to create a class that allows this kind of functionality:
foo a;
a["foo2"] = a["foo1"] = 5.0;
// This would raise a std::runtime_error because I would be checking that
// "foo3" isn't in validkeys
a["foo3"] = 4.0;
Any suggestions greatly appreciated.
SOLUTION
The following works exactly how I want it to, I even have a basic exception when you try to set or get a key that isn't in the set of valid keys:
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <stdexcept>
class myfooexception : public std::runtime_error
{
public:
myfooexception(const std::string & s)
: std::runtime_error(s + " is not a valid key.") {}
};
class foo {
public:
foo() {
for (std::set<std::string>::iterator it = validkeys.begin();
it != validkeys.end();
++it) {
data[*it] = 0.0;
}
}
const double & operator[](const std::string & key) const {
if (data.find(key) == data.end()) {
throw myfooexception(key);
} else {
return data.find(key)->second;
}
}
double & operator[](const std::string & key) {
if (data.find(key) == data.end()) {
throw myfooexception(key);
} else {
return data[key];
}
}
private:
static const std::set<std::string> validkeys;
std::map<std::string, double> data;
};
const std::set<std::string> foo::validkeys = {"foo1", "foo2"};
int main(void)
{
foo a;
a["foo1"] = 2.0;
a["foo1"] = a["foo2"] = 1.5;
// a["foo3"] = 2.3; // raises exception: foo3 is is not a valid key
const foo b;
std::cout << b["foo1"]; // should be ok
// b["foo1"] = 5.0; // compliation error, as expected: b is const.
return 0;
}
The operator [] is not declared const in the std::map, because the operator [] also inserts a new element when the key is not found and returns a reference to its mapped value. You can use the map::find method instead of map::operator[] if you want your operator[] to be const.
The subscript operator for std::map is non-const as it inserts a new element if one does not yet exist. If you want your map to have a const operator[], you need to write one that uses map::find() and tests against map::end(), handling the error case.
you are trying to modify a const object!!
please remove the const of set.const members cannot be modified once they are initialised.
You are trying to assign to the std::map but your function is declared const and also returning const. Remove both const and it should work.