I have a problem with overloading of the < operator.
I have this class:
WordEntry.h:
class WordEntry
{
public:
WordEntry(string word);
~WordEntry();
bool operator<(const WordEntry otherWordEntry);
string getWord();
private:
string _word;
};
WordEntry.cpp(I removed constructor & destructor):
string WordEntry::getWord()
{
return _word;
}
bool WordEntry::operator<(WordEntry otherWordEntry)
{
return lexicographical_compare(_word.begin(),_word.end(),otherWordEntry.getWord().begin(),otherWordEntry.getWord().end());
}
Everything is fine when I'm using it in main.cpp like that:
WordEntry w1("Der");
WordEntry w2("das");
if (w1.operator<(w2)) {
cout << "w1 > w2";
}
else
{
cout << "w2 > w1";
}
But when I call sort() on a vector with WordEntry objects, I'll get the error message
Invalid operands to binary expression ('const WordEntry' and 'const
WordEntry')
and it points to stl_algo.h.
Does anyone knows what's going on here?
Right now the argument to < is const but the member is not. This means a < comparison between 2 const WordEntry& objects will fail because it can't bind to <. You need to make the member and the argument both const
bool operator<(const WordEntry& otherWordEntry) const;
bool WordEntry::operator<(const WordEntry& otherWordEntry) const {
...
}
Note: As pointed out in the comments you should also pass WordEntry by reference
string WordEntry::getWord()
bool WordEntry::operator<(WordEntry otherWordEntry)
{
return lexicographical_compare(_word.begin(),
_word.end(),
otherWordEntry.getWord().begin(),
otherWordEntry.getWord().end());
}
The getWord member function creates a copy of the internal member attribute and returns the copy. Two consecutive calls to getWord will return two different std::string instances with the same contents, but they are different objects none the less. The lexicographical_compare function requires that the first and second arguments are iterators into the same container, and similarly the third and fourth arguments. In your case you are passing iterators into different containers (string), which will be compared inside the function and will yield undefined behavior.
The simplest solution is have getWord return a const reference to the internal std::string, in that way, the iterators will both refer to the internal object in the right hand side object.
As others have also mentioned, you should pass the WordEntry by const reference, and the operator< should be const, to improve the code. But the issue in your implementation is the mixture of iterators from different container.
Use a const reference for the rvalue and make the method const to promise the compiler you won't change the object.
bool operator<(const WordEntry& otherWordEntry) const
{
// comparison
}
You also don't need to explicitly call the operator.
Once defined for the WordEntry object you can do this:
if (w1 < w2) { // etc }
Since you aren't using a custom comparing predicate you could just use the std::string::operator<:
return _word < otherWordEntry._word;
David makes an excellent point on returning the internal member by value. If you want to use lexicographical_compare with an accessor instead of the _word member directly (which can as you're in the class scope) then you should define it like so:
const string& getWord() const { return _word; }
Related
In my class assignment I am suppose to implement member functions of a predefined class. I am confused on the purpose of these the overload on findStudent. Can this class not be designed to only contain one definition, which returns a regular iterator that can be casted as const? Or rather, use std::find since it is already overloaded to return a const_iterator?
class Class{
public:
Class(std::string name, std::string teacher, std::string capacity);
std::string getTeacher();
std::string getName();
int getCapacity();
void addStudent(std::string);
void removeStudent(std::string);
std::vector<std::string>::iterator findStudent();
std::vector<std::string>::const_iterator findStudent() const;
private:
std::string name;
std::string teacher;
int capacity;
std::vector<std::string> students;
};
The purpose of overloading those functions is that they differ by their const-ness.
Consider:
you pass Class foo; to a function
void bar(Class& foo).
If inside this function you were to call findStudent(), the non-const version of the member function would be invoked. As a result, you would get a mutating iterator of std::vector<std::string>. The iterator would allow to assign new values, clear strings and do whatever else you want with the values.
Now consider another case:
void bar(const Class& foo).
Inside this function, the const version of findStudents() will be called, and you won't be able to modify the values. You will be able to inspect them, to print them, to sum the lengths, i.e., you will be able to do only non-mutating operations on your students.
The purpose of this is to enable both the compiler and -- especially important! -- the programmer to reason about the code. Const functions do not change the state of the object, they conserve invariants of the code. For instance, it generally holds that if you call a const function twice in succession, both calls should return the same answer. (This holds "generally", but not always, especially if we are dealing with hardware).
When a member function is const-qualified, it can be called on a const object. (It can also be called on a non-const object, since a const conversion that ADDs const is a valid implicit conversion.)
Member functions that are not const qualified can only be called on non-const objects (because they can change the state of the object, defeating the meaning of const if such a conversion could happen.)
Thus:
class Foo {
public:
...
int& operator[](int idx) { return values[idx]; } // 1
int operator[](int idx) const { return values[idx]; } // 2
private:
int values[10];
};
We have two versions of operator[], one returning a mutable reference, and the other returning a copy (on which changes won't affect the object it came from.
And so
Foo f;
int& x = f[3]; // calls 1, since f is non-const
const Foo cf;
int y = cf[3]; // calls 2, since cf is const
If you want your code to work on const and non-const objects, but always return a const_iterator, then you could get by with only implementing a version of findStudent that has the const qualification, like op 2 does above, and not overload it with the non-const version.
I am guessing that findStudent is supposed to take a name (or other criteria) and return an iterator to that student, or an "end" iterator if it's not found. In that case, you'd use std::find() in the implementation of find_student. So be sure to allow callers to say what student they want to find!
I'm trying to call a function on a polymorphic item. But I get the following error message at compile time:
'this' argument to member function 'select' has type 'const SelectParam', but function is not marked const
the error is shown at the p->selection(*it)
std::set<Tuple>::iterator it;
for (it = tuples.begin(); it != tuples.end();) {
for (const SelectParam* p: selectionParams) {
bool successful = p->select(*it);
if( !successful ) {
it = tuples.erase(it);
} else {
it++;
}
}
}
and here's how those classes are defined. (I use to not have all the const and & is there but I put them everywhere I could in hopes that I would make whatever it wanted const but clearly I'm not approaching the problem right since it's not changing anything.
In one of the child classes that is being stored at parent pointer.
bool const select(Tuple const & tup) {
bool matched = false;
if (tup[idx] == val) {
matched = true;
}
return matched;
}
In the other child class that is being used with polymorphism
bool const select(Tuple const & tup) {
bool matched = false;
if (tup[idx1] == tup[idx2]) {
matched = true;
}
return matched;
}
And finally here's the parent class that's super simple.
class SelectParam {
public:
virtual const bool select( Tuple const & t) = 0;
};
Thanks in advance for being willing to help my feeble brain.
Indeed, you cannot call a non-const method a const object. But you also cannot call a non-const method through a pointer or reference to a const object (regardless of whether the referred-to object is const or not).
This means that this:
const SelectParam* ptr = whatever();
ptr->select(someTuple);
is ill-formed.
In your case, you've declared a pointer to a const SelectParam here on this line:
for (const SelectParam* p: selectionParams) {
Simply remove the const and it should work :-)
On the other hand, if select is never meant to modify the object, simply mark it as const:
virtual const bool select( Tuple const & t) const = 0;
And your code should also work.
You need to explicitly tell the compiler that your function will not modify any members:
bool const select(Tuple const & tup) const {
An object declared as const can neither be changed by a const nor a non-const member function (except for constructor and destructor). Even if it is passed by reference. There are two exceptions to this rule:
The constness can be casted away (casting the const away) but is generally not advised.
Class members can be declared using the mutable keyword. These members can be changed through member functions even if the containing object is declared const.
More can be read here:
const object - an object whose type is const-qualified, or a non-mutable subobject of a const object. Such object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.
As commented by others, one solution would be to add the const keyword to the member function in both its declaration and definition. With that, the function can be called even on const objects.
What is up with this though?
Within a member function, this is a pointer to the current object the function was called on [see].
I would like to remove an object from a stl vector if it is present.
class test
{
vector<Objects> myvector;
public:
test();
removeTest(Objects);
}
test::removeTest(Objects aObject)
{
myvector.erase(remove(myvector.begin(), myvector.end(), aTest),myvector.end());
}
However, whenever I try to compile this, I get the following error:
no match for 'operator=='
This only occurs if I use an iterator through a vector of objects of type 'Objects'. I can get it to work for the case of looking for an integer then removing this.
Anyone know why I am getting these errors? And am I using the correct method to remove an object from a stl vector of objects by value?
no match for 'operator=='
Error says it all. You need to provide an equality comparison for your Object class, std::remove can't otherwise know how to compare your objects to determine which ones need to be removed.
For example:
struct Object {
int i;
std::string s;
bool operator==(const Object& rhs) const {
return i == rhs.i && s == rhs.s;
}
};
It worked for integers because equality comparison between integers is built-in. Not so for user-defined types.
With that, this:
void test::removeTest(const Object& aObject)
// ^^^^ ^^^^^^^^^^^^^
{
myvector.erase(
remove(myvector.begin(), myvector.end(), aObject),
// ^^^^^^^
myvector.end()
);
}
Should do exactly what you want. Note that you were missing a return type for removeTest (I'm assuming you want void) and you should take the object you want to remove by reference-to-const to avoid the unnecessary copy.
I am currently working on overloading the == operator for my linked list. I have the operator in my header set up like the following:
class sqrlst
{
public:
std::vector<int> vlist;
bool operator == (iterator const & rhs )
{
return this->iter == rhs.iter;
};
I then created a method in my header file with the following code
void test()
{
bool flag;
if (vlist.begin()==vlist.begin())
{
flag=true;
}
};
};
However when this method is called it does not go to my overloaded == operator function when it hits the if statment. When I put the debug point on the overload function it says that the line will not be reached.
Any tips or suggestions are greatly appreciated. Thanks!
EDIT: vlist is a list of ints.
Well, std::vector member functions begin() and end() returns iterator of type std::vector<T>::iterator, or `std::vector<T>::const_iterator, depending on whether the vector object is const or non-const. Whatever it is, the iterator type is not defined by you. Overloading == in your class sqrlist does nothing. The overload == should be a member of vector's iterator class, which you're not allowed to edit.
Also note that vector's iterator class has already overloaded == and != operators. So when you compare iterators using ==, it is invoking a member function of vector's iterator class.
I just came across various overloading methods like type of parameter passed, varying number of parameters, return type etc. I just want to know that can I overload a function with following two version
//function which can modify member
String& MyClass::doSomething();
//constant member function
String& MyClass::doSomething() const;
Please let me know the reason behind it.
Yes you can.
If you have
MyClass m;
m.doSomething();
The non-const version will be called. When you have
const MyClass m;
m.doSomething();
The const version will be called.
The easy way to understand this, is to think of the object you are calling the method on as the 0th parameter:
e.g.: Think of
String& MyClass::doSomething();
MyClass mc;
mc.doSomething();
as a fancy, compiler supported way of doing this:
String& MyClass_doSomething(MyClass *this);
MyClass mc;
MyClass_doSomething(&mc);
Now, overloading based on constness is just a special case of overloading by parameter types:
String& MyClass::doSomething();
String& MyClass::doSomething() const;
can by thought of as something like this:
String& MyClass_doSomething(MyClass *this);
String& MyClass_doSomething(const MyClass *this);
As to the usefullness of this, the easiest examples are the begin and end methods on various std:: containers. If the vector/string/whatever is non const, and you call begin(), you'll get back an iterator that can be used to modify the contents of the original container. Clearly, this shouldn't be allowed for containers marked const, so, thanks to the const/non const overloading, calling begin() on a const vector return a const_iterator that can be used to read the contents of the vector, but not modify it.
e.g.
string nc = "Hello world";
for (string::iterator iString = nc.begin(); iString != nc.end(); ++iString)
{
cout << *iString;; // legal to read
*iString = toupper(*iString); // legal to write
}
const string c = "Hello world again";
for (string::const_iterator iString = c.begin(); iString != c.end(); ++iString)
{
cout << *iString;; // legal to read
// *iString = toupper(*iString); // Writing is illegal
}
As to overloading by return type, you can't do it in C++. However, you can simulate it fairly decently.
Yes, you can do this.
(#Job, this is not overload on return type, this is overload on 'const-state' of the called class instance)
If the instance is const, the second variant will be called, if it isn't, the first one is called.
The practical use is that if the method is called on a const instance, you probably also want to return a "const String&" (e.g. if you return a reference to a member of the class), like this:
//function which can modify member
String& MyClass::doSomething();
//constant member function
const String& MyClass::doSomething() const;
This is how iterator is used. You have a const iterator as well as a non-const one.
Not only is this possible, it as a commonly used idiom.
For example, if the const version of a function is called, a reference to a read-only version of a result can be returned. But if the non-const version is called, a copy is constructed and returned that the receiver may modify. By implementing a const version, we are saved from performing the construction when we know it isn't going to be needed.