I have a simple class that I am storing in a vector as pointers. I want to use a find on the vector but it is failing to find my object. Upon debugging it doesn't seem to call the == operator I've provided. I can 'see' the object in the debugger so I know its there. The code below even uses a copy of the first item in the list, but still fails. The only way I can make it pass is to use MergeLine* mlt = LineList.begin(), which shows me that it is comparing the objects and not using my equality operator at all.
class MergeLine {
public:
std::string linename;
int StartIndex;
double StartValue;
double FidStart;
int Length;
bool operator < (const MergeLine &ml) const {return FidStart < ml.FidStart;}
bool operator == (const MergeLine &ml) const {
return linename.compare( ml.linename) == 0;}
};
Class OtherClass{
public:
std::vector<MergeLine*>LineList;
std::vector<MergeLine*>::iterator LL_iter;
void DoSomething( std::string linename){
// this is the original version that returned LineList.end()
// MergeLine * mlt
// mlt->linename = linename;
// this version doesn't work either (I thought it would for sure!)
MergeLine *mlt =new MergeLine(*LineList.front());
LL_iter = std::find(LineList.begin(), LineList.end(), mlt);
if (LL_iter == LineList.end()) {
throw(Exception("line not found in LineList : " + mlt->linename));
}
MergeLine * ml = *LL_iter;
}
};
cheers,
Marc
Since your container contains pointers and not objects, the comparison will be between the pointers. The only way the pointers will be equal is when they point to the exact same object. As you've noticed the comparison operator for the objects themselves will never be called.
You can use std::find_if and pass it a comparison object to use.
class MergeLineCompare
{
MergeLine * m_p;
public:
MergeLineCompare(MergeLine * p) : m_p(p)
{
}
bool operator()(MergeLine * p)
{
return *p == *m_p;
}
};
LL_iter = std::find_if(LineList.begin(), LineList.end(), MergeLineCompare(mlt));
I think what you really want is to use std::find_if like this:
struct MergeLineNameCompare
{
std::string seachname;
MergeLineNameComp(const std::string &name) : seachname(name)
{
}
bool operator()(const MergeLine * line)
{
return seachname.compare( line->linename ) == 0;
}
};
LL_iter = std::find_if(LineList.begin(), LineList.end(), MergeLineNameCompare(linename) );
The operator == (no matter wich form) is better saved for real comparison of equality.
Operator overloading can't work with pointers as it is ambiguous.
Bjarne Stroustrup :-
References were introduced primarily to support operator overloading.
C passes every function argument by value, and where passing an object
by value would be inefficient or inappropriate the user can pass a
pointer. This strategy doesn’t work where operator overloading is
used. In that case, notational convenience is essential so that a user
cannot be expected to insert address− of operators if the objects are
large.
So, may be not best but still :-
std::vector<MergeLine>LineList;
std::vector<MergeLine>::iterator LL_iter;
Related
How does the std::set<T> container check if two objects are unique? I tried overriding the equality operators (==), but it didn't work.
The reason I want to do this is that I have let's say a class Person and I specify that my Person is the same person if they have the same name (maybe even birthdate, address, etc.).
In ccpreference.com, they write the following (which is a bit unclear to me):
Everywhere the standard library uses the Compare concept, uniqueness
is determined by using the equivalence relation. In imprecise terms,
two objects a and b are considered equivalent (not unique) if neither
compares less than the other: !comp(a, b) && !comp(b, a).
I assume, that this question also expands to other STL containers and even algorithms (maybe even to the whole STL). So if in future, I want to use the function std::find, I would be looking up the name of the person and not the object itself. Is this correct?
EDIT
I want to add some example code.
// My operator overloading comparing two strings.
bool operator==(Node & rhs) const {
return this->name.compare(rhs.name);
}
Then, in the UnitTest I add twice an object with the same name into the set. It is added twice (but should be the same according to the operator==.
void test_adding_two_identical_nodes() {
// The pool is a set<Node> inside
model::Node_Pool pool{};
pool.store_node(model::Node{"Peter"});
pool.store_node(model::Node{"Peter"});
// Should be only 1 because the same node should be added once into a set.
ASSERT_EQUAL(1, pool.size());
}
std::set<T> doesn't compare using ==. It compares, by default, using std::less<T>. In turn std::less<T> uses, by default, the operator <.
One way to implement a set is to override operator<, like so:
#include <set>
#include <cassert>
struct Person {
const char *name;
int uid;
};
bool operator<(const Person& a, const Person& b) {
return a.uid < b.uid;
}
int main () {
Person joe = {"joseph", 1};
Person bob = {"robert", 2};
Person rob = {"robert", 3};
Person sue = {"susan", 4};
std::set<Person> people;
people.insert(joe);
people.insert(bob);
people.insert(rob);
assert(people.count(joe) == 1);
assert(people.count(bob) == 1);
assert(people.count(rob) == 1);
assert(people.count(sue) == 0);
Person anonymous_3 = {"", 3};
assert( std::strcmp(people.find(anonymous_3)->name, "robert") == 0);
}
Alternatively, one can pass a compare operator as a template parameter when declaring the set. In the example above, this might be the compare operator:
struct Person_Compare {
bool operator()(const Person& a, const Person& b) const {
return a.uid < b.uid;
}
};
And the std::set declaration might look like this:
std::set<Person, Person_Compare> people;
The rest of the example is unchanged.
First of all, don't override comparison operators to compare anything but TOTAL equivalence. Otherwise you end up with a maintenance nightmare.
That said, you'd override operator <. You should instead give set a comparitor type though.
struct compare_people : std::binary_function<person,person,bool>
{
bool operator () ( person const& a, person const& b) const { return a.name() < b.name();
};
std::set<person, compare_people> my_set;
I overloaded array subscript ( [] ) operator. I have made it return an integer as I wont be using it for any assignment purposes. However, I am unable to use the comparison operator now!
Here is the code
class Set
{
public:
virtual int operator[](int i) = 0;
virtual int size() = 0;
void union_operation(Set* second);
void interesction_operation(Set* second);
};
void Set::union_operation(Set* second)
{
int second_size = second->size();
for(int i=0;i<second_size;i++)
{
for(int j=0;j<this->size();j++)
{
//The line below doesnt work!
if(this[j]==second[i])
{
break;
}
}
}
}
The implementation of operator overloading is carried out in a derived class.
Since the overloaded operator will return an integer, hence the comparison is between two integers, which is perfectly valid. Why does this line still give an error?
In C++, this is a pointer that requires dereferencing before you can use it. Unless you're passing it to a function of course.
So, in order for your comparison to work, it should look like the following:
if((*this)[j] == (*second)[i])
{
break;
}
EDIT: second is also a Set pointer so you must dereference it to use it too.
it has been for several days that I kept on my problem with no answer...
I'm trying to search an item in order to modify it. With a "list" I need to overload the operator== but I don't understand my mistake. Can you please tell me how can I solve this ?
class Nation{
private :
short continent;
unsigned int population, superficie;
string pays, ville;
public :
list<Nation> lireRemplir(list<Nation>liste, const char nomALire[]);
Nation(short continent, unsigned int population, unsigned int superficie, string pays, string ville) {
..... // ok
}
Nation(){};
void modifierContinent(list<Nation> liste, string nomPays, short nouveauContinent);
bool operator == (Nation &); //?
};
bool Nation::operator == (Nation & autre) {
return this->pays == autre.pays;
}
void modifierContinent(list<Nation> liste, string nomPays, short nouveauContinent)
{
//Nation uneNation(0,0,0,nomPays,"");
for (list<Nation>::iterator il = liste.begin(); il != liste.end(); il++)
{
if (*il == nomPays){ cout << "found!"; }
}
}
int main()
{
list<Nation>liste;
liste=lireRemplir(liste, "Nation.txt"); //hidden but working
modifierContinent(liste, "FRANCE", 5);
}
Here:
if (*il == nomPays){ cout << "found!"; }
nomPays is type string, yet you overloaded the operator for another Nation type. There is no overloaded = that takes Nation and string.
Two solutions:
You can either overload a conversion to string (not recommended) or create a constructor that takes string.
The best solution is to create a getter method for pays just to do il->getPays() == nomPays.
Clear and concise.
You do not necessarily have to overload an operator in your class. Here is why: You already use std::list. That's good. Now go one step further and also ditch your self-made algorithm in favour of the standard one: std::find_if.
std::find_if can search a standard container class using a comparison functor provided by you. Often, that functor is a struct with an overloaded operator() (so objects of it can be used like functions).
I'll give you an example:
#include <algorithm> // for std::find_if
// ...
struct CountryComparison
{
CountryComparison(std::string const &country) : m_country(country) {}
bool operator()(Nation const &nation) const
{
return nation.pays == m_country;
}
std::string m_country;
};
void modifierContinent(list<Nation> &liste, string const &nomPays, short nouveauContinent)
{
list<Nation>::const_iterator find_iter = std::find_if(liste.begin(), liste.end(),
CountryComparison(nomPays));
if (find_iter != liste.end())
{
cout << "found!";
}
}
I've also made sure strings are passed by const&, which should be the default for string arguments at least pre-C++11. And I pass liste by &, which is also more likely the intended behaviour (as it does not create needless copies).
By the way, your Nation class is strange. It contains a "country" (pays) and a "town" (ville). This means that in your class design, a nation consists of a country and a town. That doesn't make sense, except maybe for city states ;)
Edit: I forgot an implementation detail. As the functor cannot directly access the pays member of Nation, consider giving your class a member function like:
std::string GetPays() const
{
return pays;
}
Or make the functor a friend of Nation.
In the book of "The C++ Programming Language", the author gave the following example along with several statements:
Defining an operator, such as [], to be used for both reading and writing is difficult where it is not acceptable simply to return a reference and let the user decide what to do with it.
Cref, is to help implement a subscript operator that distinguishes between reading and writing.
Why [] is difficult to be defined when to be used for both reading and writing?
How does the definition of class Cref help to solve this issue?
class String{
struct Srep;
Srep *rep;
public:
class Cref;
// some definitions here
void check (int i) const { if (i<0 || rep->sz<=i) throw Range( );}
char read( int i) const {return rep->s[i];}
void write(int i, char c){ rep=rep->get_own_copy(); rep->s[i]=c;}
Cref operator[] (int i){ check(i); return Cref(*this, i);}
char operator[] (int i) const{check(i); return rep->s{i];}
}
class String::Cref{
friend class String;
String& s;
int i;
Cref(String& ss, int ii): s(ss),i(ii) {}
public:
operator char( ) { return s.read(i);}
void operator=(char c){s.write(i,c);}
};
If you don't define a class Cref that solves this issue, then you have to do what std::map does:
template class <K,V> class map{
V& operator[](K const & key);
}
This returns a reference, which must be backed by a valid memory location, and therefore
std::map<string,string> m;
m["foo"];
assert(m.find("foo") != m.end());
The assertion will succeed (meaning, "foo" is now a valid key in the map) even though you never assigned something to m["foo"].
This counterintuitive behavior can be fixed by the Cref class in your example -- it can perform the appropriate logic to create m["foo"] only when you assign to the reference, and ensure that m.find("foo") == m.end() if you didn't perform some assignment when you tried to read the nonexistant m["foo"].
Likewise, in your String class (which is a reference-counted string -- strings share their string data, and a new copy is created when you change a string whose data is shared with another string), you'd have to make a copy when using operator[] to read characters. The use of the Cref class, allows you to ensure that you only make a copy when using operator[] to write.
String s;
s[0] = 5;
will call String::operator [](int) and then String::Cref::operator =(char).
However,
String s;
char c = s[0];
will call String::operator [](int) and then String::Cref::operator char().
When reading, String::Cref::operator char is called, and when writing String::Cref::operator = is called - this allows you to distinguish between reading and writing.
Why [] is difficult to be defined when to be used for both distinguish between reading and writing?
It's because the non-const operator[] is called whenever you have a non-const object, even if you're using it in a read-only fashion.
This is a homework assignment. The Field container was the assignment from a week ago, and now I'm supposed to use the Field container to act as a dynamic array for a struct NumPair which holds two char * like so:
struct NumPair
{
char *pFirst, *pSecond;
int count;
NumPair( char *pfirst = "", char *psecond = "", int count = 0)
: pFirst(strdup(pfirst)), pSecond(strdup(psecond)), count(count)
{ }
NumPair( const NumPair& np )
: count(np.count), pFirst(strdup(np.pFirst)), pSecond(strdup(np.pSecond))
{ }
NumPair& operator=( const NumPair& np )
{
if(this != &np)
{
pFirst = strdup(np.pFirst);
pSecond = strdup(np.pSecond);
count = np.count;
}
return *this;
}
and the Field container
Field<NumPair> dict_;
The homework requires the use of char *, and not string, so that we can get better with all this low-level stuff. I've already had some question about char to wchar_t conversions, etc.
Now I have a question as to whether or not I'm destructing the NumPair properly. The scenario is as follows:
1) Field destructor gets called
template <class T>
Field<T>::~Field()
{
delete[] v_;
}
2) Delete calls the destructor of every element NumPair in v_;
~NumPair()
{
free(pFirst);
free(pSecond);
}
Is this okay? I haven't really read too many articles about mixing and matching elements created on the heap and free-store as we wish. I figure as long as I don't use delete on an improper malloc'ed element, I should be fine.
However, I don't know the entire intricacies of the delete command, so I'm wondering whether or not this is valid design, and what I could do to make it better.
Also, of course this isn't. I'm getting an error of the type:
This may be due to a corruption of the heap and points to dbgheap
extern "C" _CRTIMP int __cdecl _CrtIsValidHeapPointer(
const void * pUserData
)
{
if (!pUserData)
return FALSE;
if (!_CrtIsValidPointer(pHdr(pUserData), sizeof(_CrtMemBlockHeader), FALSE))
return FALSE;
return HeapValidate( _crtheap, 0, pHdr(pUserData) ); // Here
}
Again, how could I improve this without the use of string?
FIELD CTOR/Copy Ctor/Assignment
template <class T>
Field<T>::Field()
: v_(0), vused_(0), vsize_(0)
{ }
template <class T>
Field<T>::Field(size_t n, const T &val)
: v_(0), vused_(n), vsize_(0)
{
if(n > 0)
{
vsize_ = 1;
while(vsize_ < n)
vsize_ <<= 1;
v_ = new T[vsize_];
std::fill(v_, (v_ + vused_), val);
}
}
template <class T>
Field<T>::Field(const Field<T> &other)
: v_(new T[other.vsize_]), vsize_(other.vsize_), vused_(other.vused_)
{
std::copy(other.v_, (other.v_ + other.vused_), v_);
}
template <class T>
Field<T>& Field<T>::operator =(const Field<T> &other)
{
this->v_ = other.v_;
this->vused_ = other.vused_;
this->vsize_ = other.vsize_;
return *this;
}
FIELD MEMBERS
T *v_;
size_t vsize_;
size_t vused_;
Your copy constructor (of Field<>) seems OK, but the operator= is problematic.
Not only does it leak memory (what happens to the original v_?), but after that, two instances of Field<> hold a pointer to the same block of memory, and the one that is destructed first will invalidate the others v_ - and you can't even tell whether that has happened.
It's not always easy to decide how to deal with operator= - some think that implicit move semantics are okay, but the rest of us see how that played out with the majority of people, with std::auto_ptr. Probably the easiest solution is to disable copying altogether, and use explicit functions for moving ownership.
Your string handling in NumPair looks ok (strdup + free) and your Field container delete[] looks okay but it's hard to say because you don't show what v_ is.
eq mentions in a comment that you should also beware of how you are copying NumPairs. By default, C++ will give you an implicit member-wise copy constructor. This is where a RAII type like std::string makes your life easier: Your std::string containing struct can be copied without any special handling on your part and memory referenced in the string will be taken care of by the string's copy. If you duplicate your NumPair (by assigning it or returning it from a function for example) then the destruction of the temporary will free your strings out from under you.
Your copy constructor for Field just copies the pointers in v_. If you have two copies of a Field, all of the NumPairs in v_ will be deleted when the first Field goes out of scope, and then deleted again when the second one does.