I am trying to use std::equal_range with the structure below I have compilation error saying that error: no match for ‘operator<’ .
struct MyFoo {
int v_;
string n_;
bool operator<(int v) const
{ return v_ < v;}
};
vector<MyFoo> data;
// data is sorted by int v_
typedef vector<MyFoo>::iterator Ptr;
std::pair< Ptr, Ptr > pr = std::equal_range(data.begin(), data.end(), 10);
I've looked into the template implementatino and what is failing is the following where *it is deferenging the iterator pointing to an object of MyFoo and val_ is 10.
if(*it < val_) {
...
}
Why it is not working? I thought probably because it is trying to call the the global operator< that is not defined, but since I defined it as class member that should not be a problem, isn't it?
Provide non-member comparison operators :
bool operator<(int v, const MyFoo& foo)
{
return foo.v_ < v;
}
bool operator<(const MyFoo& foo, int v)
{
return v < foo;
}
Alternatively, you can provide a conversion operator to int :
operator int() cont {return v_;}
Which is probably unwanted, since the compiler will be able to perform silent conversions in other places of your code.
As an other alternative: provide
bool operator<(const MyFoo& rhs) const { return v_ < rhs.v_; }
And use std::equal_range on a dummy object with correct v_ as:
std::pair<Ptr, Ptr> pr = std::equal_range(data.begin(), data.end(), MyFoo{10, ""});
You may be having trouble because the std::equal_range implementation uses std::less. This is going to try to convert your MyFoo to an int to do the comparison, rather than just using an operator<() overload. Try adding this to your MyFoo class...
operator int() const
{
return v_;
}
Related
Say I have the following struct:
struct Parameter {
double value;
double error;
};
So that I'm usually working with vectors of that struct (ie. std::vector<Parameter>), and ocasionally I want to set a vector of values (but not errors) in that vector of parameters by using the operator= with a standard std::vector, for convenience.
std::vector<Parameter> vector_of_parameters;
std::vector<double> vector_of values;
....
vector_of_parameters = vector_of_values;
To do so, I'm trying to overload operator= for this struct as follows:
std::vector<Parameter> operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
But this will return an error saying that std::vector operator=(const std::vector& v) must be a non-static member. So if I understand it correctly, I have to define this as a member function of the operator as:
std::vector<Parameter>::operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
The error now says that a syntaxis with template<>, but I dont really see it, or understand it, and don't know what more can I do.
You cannot overload the assignment operator of std::vector. operator = must be a member function and you just can't add a member function to std::vector.
What you can do is make a convenience function like create_parameters that takes a std::vector<double> and returns a std::vector<Parameter>. That would look like
std::vector<Parameter> create_parameters(std::vector<double> const& params)
{
std::vector<Parameter> ret(params.size());
std::transform(params.begin(), params.end(), ret.begin(),
[](auto value) { return Parameter{value, 0}; });
return ret;
}
and then
vector_of_parameters = vector_of_values;
would become
vector_of_parameters = create_parameters(vector_of_values);
I think that an alternative simple way to create std::vector<Parameter> from std::vector<double> is defining a single argument constructor Parameter(double value) which accepts the Parameter::value:
#include <vector>
#include <optional>
struct Parameter
{
double value;
std::optional<double> error;
explicit Parameter(double v) : value(v)
{}
};
Then you can use range-constructor as follows:
DEMO
std::vector<Parameter> v_of_parameters(v_of_values.cbegin(), v_of_values.cend());
Using functors with parameters generally looks like that:
// Definition:
struct some_functor_with_params
{
int ref;
explicit some_functor_with_params(int ref) : ref(ref) {}
bool operator ()(int i) const {return i == ref;}
};
// Usage:
std::vector<int> v;
std::find_if(v.begin(), v.end(), some_functor_with_params(42));
For a functor without parameters, the code may become
// Definition:
struct some_functor_without_params
{
bool operator ()(int i) const {return i == 42;}
};
// Usage:
std::vector<int> v;
std::find_if(v.begin(), v.end(), some_functor_without_params());
but I would prefer the following usage:
std::vector<int> v;
std::find_if(v.begin(), v.end(), some_functor_without_params); // no parens
which has the following advantages:
more concise
more readable when invoking the functor directly: some_functor_without_params(i) rather than some_functor_without_params()(i)
interchangeable with a function: bool some_functor_with_params(int i) {return i == 42;}
I implemented it the following way in a header file:
namespace {
struct
{
bool operator ()(int i) const {return i == 42;}
} some_functor_without_params;
}
I think the struct does not need a name since it has no user-declared constructor (nor destructor, nor anything requiring the struct name). I put the object in an unnamed namespace so that each compilation unit has its own some_functor_without_params and there is no 'double definition' link error.
Is there any performance penalty or any other drawback I cannot see?
This approach worked as expected until I encountered a very strange compilation error with Visual C++ 2013 which disappeared when naming the functor type, i.e. replacing
struct
{
bool operator ()(int i) const {return i == 42;}
} some_functor_without_params;
with
struct some_functor_without_params_t
{
bool operator ()(int i) const {return i == 42;}
} some_functor_without_params;
The error occurs only in Debug, not in Release, and states
error C2039: '<unnamed-type-some_functor_without_param>': is not a member of 'my_namespace:: ?? A0xbf2cc73f'
in file xstring, here:
_Myt& operator+=(const _Elem *_Ptr)
{ // append [_Ptr, <null>) // <-- on this line(!)
return (append(_Ptr));
}
It looks like a compiler bug, what do you think?
I want to populate a std::set of GraphNode objects and check if another GraphNode with the same value exists in the set. In Java, objects can be compared by overloading equals and compareTo methods, instead of creating some functor object. I implemented operator==(T& t) and expected to find the object in the set like this,
std::find(nodesSet->begin(),nodesSet->end(), new GraphNode<T>(1))!=nodesSet->end())
But I am not getting the break point in neither == nor ()() operator functions. Why is it so? Is there a way to find the object by object comparison?
template<class T>
class GraphNode
{
friend class Graph<T>;
friend bool operator==(GraphNode<T>& node1, GraphNode<T>& node2);
private:
T t;
std::vector<GraphNode<T>*> adjNodes;
public:
bool operator==(T& t);
};
template<class T>
inline bool GraphNode<T>::operator==(T & t)
{
return this->t == t ? true : false;
}
template<class T>
inline bool operator==(GraphNode<T>& node1, GraphNode<T>& node2)
{
return node1.t == node2.t ? true : false;
}
void populate()
{
std::set<GraphNode<T>*>* nodesSet = new set<GraphNode<T>*>;
nodeSet->insert(new GraphNode<T>(1));
nodeSet->insert(new GraphNode<T>(2));
if ( std::find( nodesSet->begin(),nodesSet->end(),
new GraphNode<T>(1) ) != nodesSet->end() )
{
cout<<"found value";
}
}
As aschepler pointed out, the problem with your code is that you end up comparing pointers, not objects. std::find (look at the possible implementations in the linked page), if called without a predicate, uses the == operator to compare what is returned when the iterators you give it are dereferenced. In your case, you have a std::set<GraphNode<T>*> nodesSet, so the type of *nodesSet.begin() is GraphNode<T>*, not GraphNode<T> (note the lack of star). In order for you to be able to use the == operator defined for your GraphNode, you need to have your set be std::set<GraphNode<T>>, that is of objects of your type rather than of pointers.
If you have to store pointers in your set (e.g. because you don't want to copy the objects), you can write a wrapper for pointers that uses the comparison operator for the underlying class of the pointers. Here's an example:
#include <iostream>
#include <set>
#include <algorithm>
class obj {
int i;
public:
obj(int i): i(i) { }
bool operator<(const obj& o) const { return i < o.i; }
bool operator==(const obj& o) const { return i == o.i; }
int get() const { return i; }
};
template <typename T>
class ptr_cmp {
T* p;
public:
ptr_cmp(T* p): p(p) { }
template <typename U>
bool operator<(const ptr_cmp<U>& o) const { return *o.p < *p; }
template <typename U>
bool operator==(const ptr_cmp<U>& o) const { return *o.p == *p; }
T& operator*() const { return *p; }
T* operator->() const { return p; }
};
int main(int argc, char* argv[])
{
obj five(5), seven(7);
std::set<ptr_cmp<obj>> s;
s.insert(&five);
s.insert(&seven);
obj x(7);
std::cout << (*std::find(s.begin(),s.end(), ptr_cmp<obj>(&x)))->get()
<< std::endl;
return 0;
}
It turned out that my compiler (gcc 6.2.0) required both operator== and operator< for std::find to work without a predicate.
What is wrong with using a predicate though? It is a more generalizable approach. Here's an example:
#include <iostream>
#include <set>
#include <algorithm>
class obj {
int i;
public:
obj(int i): i(i) { }
bool operator==(const obj& o) const { return i == o.i; }
int get() const { return i; }
};
template <typename T>
struct ptr_cmp {
const T *l;
ptr_cmp(const T* p): l(p) { }
template <typename R>
bool operator()(const R* r) { return *l == *r; }
};
template <typename T>
ptr_cmp<T> make_ptr_cmp(const T* p) { return ptr_cmp<T>(p); }
int main(int argc, char* argv[])
{
obj five(5), seven(7);
std::set<obj*> s;
s.insert(&five);
s.insert(&seven);
obj x(7);
std::cout << (*std::find_if(s.begin(),s.end(), make_ptr_cmp(&x)))->get()
<< std::endl;
return 0;
}
Note, that make_ptr_cmp allows you to avoid explicitly stating the type, so you can write generic code.
If you can use C++11, use can just use a lambda function instead of ptr_cmp,
std::find_if(s.begin(),s.end(), [&x](const obj* p){ return *p == x; } )
std::find compares the values pointed at by the iterators. These values are pointers, not objects. So none of them will be equal to new GraphNode<T>(1), which is a brand new pointer to a brand new object.
As others have stated, you are comparing pointers, which won't work as expected, it's doing comparisons on addresses in memory. The operation a < b has a valid meaning for a pointer but will order the elements by their location in memory, not on their contained data elements and also no elements will be unique, as they will all have unique addresses. That is unless you try to insert the same element twice.
The above issue however will be hidden by using std::find, which iterates over all the elements in the container anyway. If you are using a set, you should be aspiring to get logarithmic time look ups for elements, so should use sets own find function, which knows that its a binary tree under the hood.
In C++, the equivalent of Object#equals is operator== (as you knew) and in the context of associative containers the equivalent of Object#compareTo is operator<. Object#equals and operator== work in the same way, exactly as you expect; If somethings equal its equal, simple to understand. Object#compareTo and operator< are used by algorithms in different ways, operator< is used to implement strict weak ordering to determine if one element is less than or greater than another.
So to allow your elements to be usable in a set you will need an overridden operator< in your GraphNode class. Once you have this you can use the std::set::find function to find elements in your set and it will find them in O(log n) time rather than linear time.
These algorithms are designed on the assumption they are working on value types, i.e not pointers but those things that are pointed to. So to use pointers you need to define a new comparison function that basically dereferences the pointers before applying the comparison (either == or <).
Some example code
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
template<typename>
class Graph
{
};
template<class T>
class GraphNode
{
friend class Graph<T>;
friend bool operator==(const GraphNode<T>& a, const GraphNode<T>& b);
private:
T t;
std::vector<GraphNode<T>*> adjNodes;
public:
explicit GraphNode(const T& tval)
:t(tval)
{}
T& getT(){ return t; }
const T& getT() const { return t; }
bool operator==(const T& t);
friend bool operator<(const GraphNode& a, const GraphNode& b){
return a.t < b.t;
}
};
template<class T>
inline bool GraphNode<T>::operator==(const T& t)
{
return (this->t == t);
}
template<class T>
inline bool operator==(const GraphNode<T>& a, const GraphNode<T>& b)
{
return (a.t == b.t);
}
int main()
{
using IntGraphNode = GraphNode<int>;
std::set<IntGraphNode> nodesSet;
nodesSet.insert(IntGraphNode(1));
nodesSet.insert(IntGraphNode(2));
auto findit = nodesSet.find(IntGraphNode(1));
if(findit != nodesSet.end())
{
std::cout << "found value\n";
}
auto findit2 = std::find_if(
nodesSet.begin(),
nodesSet.end(),
[](IntGraphNode i) { return i.getT() == 1;});
if(findit2 != nodesSet.end())
{
std::cout << "found value aswell\n";
}
}
The first search uses sets own find function and the second uses std::find_if, which takes a predicate (function that returns either true or false) to test equality. The second example also removes the need to make a dummy object, by exposing the T object and using that in the comparison lambda function.
Also a comment about
std::find(nodesSet->begin(),nodesSet->end(), new GraphNode<T>(1))!=nodesSet->end())
There are quite a few conceptual misunderstandings in this line. Firstly std::find does not take a comparison function, that would be std::find_if, but the compiler will tell you that (in its own especially indirect and verbose way). Also the comparison function is evaluated in the algorithm, you are trying to evaluate it at the call site. The other thing is unlike java, you can't just fire off newed objects into oblivion. That's a memory leak, you no longer have any variable storing the newed value, so you can't delete it.
When declaring a std::map with a custom class is there any difference if I use a comparator as opposed to just implementing the less than operator for the class? Is one 'wrong' or considered bad?
Any difference between this:
std::map <MyClass, float, CompareMyClass> myMap2;
And this:
std::map <MyClass, float> myMap; // where MyClass has the less than operator implemented
Full Code:
class MyClass
{
public:
bool operator()(const MyClass& a, const MyClass& b) const
{
return a.value_ < b.value_;
}
bool operator<(const MyClass& myClass) const
{
return value_ < myClass.value_;
}
private:
friend struct CompareMyClass;
float value_;
};
struct CompareMyClass
{
bool operator()(const MyClass& a, const MyClass& b) const
{
return a.value_ < b.value_;
}
};
int main(int argv, char** args)
{
std::map <MyClass, float> myMap;
MyClass a;
myMap[a] = 1.99;
std::map <MyClass, float, CompareMyClass> myMap2;
MyClass b;
myMap[b] = 1.99;
system("PAUSE");
return 0;
}
If you provide an operator<, its behaviour should not be surprising. Now, for different uses, you might want to sort a record by different criteria, and for such a record, any operator< would always have surprising behaviour. In such a case, it is better to create a comparator who's name already gives a hint how it sorts, because it makes it explicit how a map (or other things) are sorted.
Here's a simplified version of my problem. I have a property class. It has data like has_initalized and such which i removed for this example.
When i call a function which uses T its fine. However T& isnt so i decided to write a T& version of it. But this causes all functions which uses plain T to get a compile error. Why is T& interfering with that? For this example how do i get both functions (Q and W) to work without changing main()?
template <class T>
class Property {
T v;
Property(Property&p) { }
public:
Property() {}
T operator=(T src) { v = src; return v; }
operator T() const { return v; }
operator T&() const{ return v; }
T operator->() { return v; }
};
class A{};
void Q(A s){}
void W(A& s){}
int main(){
Property<A> a;
Q(a);
W(a);
}
There is nothing in the overloading rules of C++ which allows the compiler to choose between operatorT() and operatorT&() in the call to Q. So removing the
operator T() const { return v; }
will also remove the ambiguity. But then you'll have a problem because returning a non const reference to a member in a const function is not possible.
For your Q, you can use both conversion functions. You can make the compiler prefer one over the other by making one non-const.
operator T() const { return v; }
operator T&() { return v; }
Now for Q, the operator T& is taken. This way will also fix the call to W to get a non-const reference. You can also return a const reference from the other
operator T const&() const { return v; }
operator T&() { return v; }
This way will still prefer the second conversion function for Q, but if your object a is const and you initialize a const reference, you won't always require to copy v.