I have a URL class that overloads the ==, <, >, and != operators for simple comparison. The URL class has a string data member and some functions to act on the string. The operators work fine when tested with the URL class.
I also have a Page class that has a URL data member. I am trying to overload the same operators in the Page class. Equality in the Page class is based on equality of their respective URLs, so I use the URL class boolean operators in comparing pages. This creates some compiler errors that I cannot figure out. Code for URL operators:
bool URL::operator ==(URL & u) const {
//url is the string instance variable
return url == u.GetURL();
}
Code for Page operators:
bool Page::operator ==(Page & p) const {
//url is the URL instance variable of the Page class
return url == p.GetURL();
}
This produces errors like so:
src/Page.cpp: In member function ‘bool Page::operator==(Page&) const’:
src/Page.cpp:21: error: no match for ‘operator==’ in ‘((const Page*)this)->Page::url == Page::GetURL()()’
inc/URL.h:118: note: candidates are: bool URL::operator==(URL&) const
I predict that it is something dumb that I am forgetting. Will you prove me right?
edit: Const correctness has bitten me in the bum. Thanks for the help.
It should have been:
bool URL::operator ==(const URL & u) const {
//url is the string instance variable
return url == u.GetURL();
}
And analogously for the other operators.
If you still get compiler errors, perhaps you haven't made GetURL() const as well:
std:string URL::GetURL() const {
// whatever...
}
I would also like to point out that methods (ie the public interface) are there to protect external entities from changes in the implementation details. Also that a class is automatically a friend of itself (for the same reason) and thus just accessing the members of the other object is OK.
bool URL::operator ==(URL & u) const {
//url is the string instance variable
return url == u.GetURL();
}
Can be written like:
bool URL::operator ==(URL & rhs) const
{
return url == rhs.url; // No need to use GetURL()
}
In my mind this makes the code clearer (but this again is an opinion your tastes may vary)
Related
I am new programmer of c++. I have encountered a proble and I cannot understand this. Could you please help me figure out it? This is an example of the book,<c++ primer>.
class TextQuery {
public:
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream &);
QueryResult query(const std::string &) const;
private:
std::shared_ptr<std::vector<std::string>> file;
std::map<std::string,std::shared_ptr<std::set<line_no>>> wm;
//static std::string cleanup_str(const std::string &); // the book example uses 'static'
std::string cleanup_str(const std::string &); // I think 'static' key word is not needed.so i remove it.
};//class declearation ends here.
std::string TextQuery::cleanup_str(const std::string &word) {
string ret;
for(auto it = word.begin();it != word.end();++it){
if(!ispunct(*it))
ret += tolower(*it);
}
return ret;
}
QueryResult TextQuery::query(const std::string &sought) const
{
static shared_ptr<set<line_no>> nodata(new set<line_no>); //line 66
auto loc = wm.find(cleanup_str(sought)); //line 67
if(loc == wm.end())
return QueryResult(sought,nodata,file);
else
{
return QueryResult(sought,loc->second,file);
}
}
I cannot unstandard the difference between the version of removing 'static' key word and that does not. The compile error is:
passing 'const TextQuery' as 'this' argument discards qualifiers [-fpermissive],67
the object has type qualifiers that are not compatible with the member function "TextQuery::cleanup_str" -- object type is: const TextQuery,67
I have tried two ways that can work correctly:
add 'static' to the function cleanup_str. I cannot unstand why it can pass.
The other way I tried is: remove the last 'const' key word of function QueryResult TextQuery::query(const std::string &sought) const, make it become: QueryResult TextQuery::query(const std::string &sought) . And this method works, I also cannot understand this reason.
const method has a contract - it does not change internal state of an instance of the class. Technically it is implemented the way that hidden parameter this has type const TextQuery * - pointer to constant (non modifiable) object of TextQuery. To follow that contract you cannot call non constant method, it may modify internal state and brake contract of original, const method. Now -
add 'static' to the function cleanup_str. I cannot unstand why it can pass.
Static method does not work with an instance, it just belongs to that class (it does not have hidden parameter this at all) hence it is safe to call such method from const method, contract would not be broken. If you remove static then that method becomes regular non const method and it is not safe to call it anymore from const one.
The other way I tried is: remove the last 'const' key word of function QueryResult
Now your method query becomes non constant (type of this is TextQuery *) so it is safe to call non constant or static method from it. So your compiler error disappear.
I have come across various container libraries in modern day C++ that have found a way to override the operator[] and still return multiple types. For example, using nlohmann::json, the following code is all valid:
const nlohmann::json settings;
// set some values:
j["pi"] = 3.141;
j["happy"] = true;
// get some values:
std::string deviceName = settings["device"];
bool yesOrNo = settings["blah"];
How is this possible, especially in the case cases? In my own attempts, I've run into the common error case of "could not deduce template argument for 'T'". I do think it has to do with some proxy object (likely value_t or object_t), however I haven't been able to follow the template logic back deep enough in the case of nlohmann's json implementation (which is pretty impressive!).
If you look at the declaration of nlohmann::json::operator[], it returns a reference, which is defined as a value_type&, where value_type is defined as basic_json. The basic_json class has a templated conversion operator that can convert a json value to any type that the basic_json::get() method supports, which includes booleans, integers, strings, etc. So, what you are essentially doing is this:
//std::string deviceName = settings["device"];
std::string deviceName = settings["device"].operator std::string();
//which is effectively
//std::string deviceName = settings["device"].get<std::string>();
//bool yesOrNo = settings["blah"];
bool yesOrNo = settings["blah"].operator bool();
// which is effectively
//bool yesOrNo = settings["blah"].get<bool>();
I have a routine that does some moderately expensive operations, and the client could consume the result as either a string, integer, or a number of other data types. I have a public data type that is a wrapper around an internal data type. My public class looks something like this:
class Result {
public:
static Result compute(/* args */) {
Result result;
result.fData = new ExpensiveInternalObject(/* args */);
return result;
}
// ... constructors, destructor, assignment operators ...
std::string toString() const { return fData->toString(); }
int32_t toInteger() const { return fData->toInteger(); }
double toDouble() const { return fData->toDouble(); }
private:
ExpensiveInternalObject* fData;
}
If you want the string, you can use it like this:
// Example A
std::string resultString = Result::compute(/*...*/).toString();
If you want more than one of the return types, you do it like this:
// Example B
Result result = Result::compute(/*...*/);
std::string resultString = result.toString();
int32_t resultInteger = result.toInteger();
Everything works.
However, I want to modify this class such that there is no need to allocate memory on the heap if the user needs only one of the result types. For example, I want Example A to essentially do the equivalent of,
auto result = ExpensiveInternalObject(/* args */);
std::string resultString = result.toString();
I've thought about structuring the code such that the args are saved into the instance of Result, make the ExpensiveInternalObject not be calculated until the terminal functions (toString/toInteger/toDouble), and overload the terminal functions with rvalue reference qualifiers, like this:
class Result {
// ...
std::string toString() const & {
if (fData == nullptr) {
const_cast<Result*>(this)->fData = new ExpensiveInternalObject(/*...*/);
}
return fData->toString();
}
std::string toString() && {
auto result = ExpensiveInternalObject(/*...*/);
return result.toString();
}
// ...
}
Although this avoids the heap allocation for the Example A call site, the problem with this approach is that you have to start thinking about thread safety issues. You'd probably want to make fData an std::atomic, which adds overhead to the Example B call site.
Another option would be to make two versions of compute() under different names, one for the Example A use case and one for the Example B use case, but this isn't very friendly to the user of the API, because now they have to study which version of the method to use, and they will get poor performance if they choose the wrong one.
I can't make ExpensiveInternalObject a value field inside Result (as opposed to a pointer) because doing so would require exposing too many internals in the public header file.
Is there a way to make the first function, compute(), know whether its return value is going to become an rvalue reference or whether it is going to become an lvalue, and have different behavior for each case?
You can achieve the syntax you asked for using a kind of proxy object.
Instead of a Result, Result::compute could return an object that represents a promise of a Result. This Promise object could have a conversion operator that implicitly converts to a Result so that "Example B" still works as before. But the promise could also have its own toString(), toInteger(), ... member functions for "Example A":
class Result {
public:
class Promise {
private:
// args
public:
std::string toString() const {
auto result = ExpensiveInternalObject(/* args */);
return result.toString();
}
operator Result() {
Result result;
result.fData = new ExpensiveInternalObject(/* args */);
return result;
}
};
// ...
};
Live demo.
This approach has its downsides though. For example, what if, instead you wrote:
auto result = Result::compute(/*...*/);
std::string resultString = result.toString();
int32_t resultInteger = result.toInteger();
result is now not of Result type but actually a Result::Promise and you end up computing ExpensiveInternalObject twice! You can at least make this to fail to compile by adding an rvalue reference qualifier to the toString(), toInteger(), ... member functions on Result::Promise but it is not ideal.
Considering you can't overload a function by its return type, and you wanted to avoid making two different versions of compute(), the only thing I can think of is setting a flag in the copy constructor of Result. This could work with your particular example, but not in general. For example, it won't work if you're taking a reference, which you can't disallow.
I ran across a class with a member var as a reference (to a std::istream), with operator void *() and bool operator !() that return that reference, and i'm wondering what that would be for. The class is related to reading/parsing text files with config param pairs. I've pulled out from the (much) larger project the basic parts. In qt (MSVC 2015 community tool chain), i had to change the operator void *() to get a compile, but seems ok on the original linux system.
(In my desktop environment i get: "error: C2440: 'return': cannot convert from 'std::istream' to 'void *'", so i replaced with a call to if(m_in.eof()) and return nullptr)
class LR { // (line reader)
public:
LR(const std::string &filename);
.... other stuff
operator void *() const { return &m_in; }
bool operator !() { return !m_in; }
LR & operator>>(std::string &line);
private:
std::istream &m_in; // input stream
std::ifstream m_in_file; // input file (if specified)
};
LR::LR(const std::string &filename, ... other stuff) :
: m_in(m_in_file)
{
// .... other stuff
if(filename.size() > 0)
{
m_in_file.open(filename.c_str());
}
// .... other stuff
}
and the class that uses this:
class CR { // config reader
public:
// .... other stuff
void Load_Variable(const std::string §ion, value, etc...);
private:
LR m_reader;
};
void CR::Load_Variable(const std::string §ion, value, etc.) {
string line;
bool found = false;
while (m_reader >> line)
{
// .... check stuff, etc.
}
}
Debugging in Qt, while (m_reader >> line) calls the operator void *().
My questions:
Why use a member var reference to a std::istream like this?
What's the purpose of returning the address of member var &m_in when it's always valid because it's a member var (or is this not true?)
Would operator *() of m_reader ever return false? I've searched a bit online and not found any similar examples of this kind of use of operators on member var refs. I need to look next at what it does when the file open fails.
Possibly this code originally used heap pointer vars or some other approach for the m_in var and it was changed somewhere along the way to be a normal member var, with the operators then edited to this? I think the history is not easy to get.
Thanks for the help, stackoverflow is awesome.
The istream has a flag that indicates if an error has occurred and it overrides the ! operator for easy access. You'll often see it used like this:
myStream >> line;
if(!myStream)
cout<<"Error reading data"<<endl;
So you are not returning a reference, you are returning a Boolean flag. Think of the ! operator as isNoError() accessor.
Your class is doing the same thing, just passing through the result from the stream it wraps.
The * operator is probably there as backward compatibility measure. Probably the existing code base is expecting a pointer and this was added so that the existing code base works with the new implementation.
I'll show my code first then explain my issue:
std::vector<std::unique_ptr<SGUIObject> > m_objects;
const std::unique_ptr<SGUIObject>& SGUIManager::getObject(const std::string& object_name)
{
for (auto const& iter : m_objects)
{
if (iter.get()->getObjectName() == object_name)
return iter;
}
}
//SButton is derived from SGUIObject
//m_clicked is a boolean member in SButton (private)
//isClicked is a public member method of SButton
const bool isClicked() const { return m_clicked; }
if (dynamic_cast<SButton>(SSceneManager::getGUIManager().getObject("testbutton").isClicked()))
std::cout << "Clicked!" << std::endl;
I just copy pasted from several different files, so it looks weird when all put together. Anyways, what I'm trying to do is downcast from a SGUIObject to a SButton and call isClicked() in an if/else loop. When I do my current code, Code::Blocks gives me this error:
error: 'const class std::unique_ptr' has no member named 'isClicked'|
I have a feeling I'm having a slight syntactical issue, and I'd be extremely grateful if someone was to explain it to me.
Thanks!
I think you mean:
dynamic_cast<SButton*>(SSceneManager::getGUIManager().getObject("testbutton").get())->isClicked()
You want to call isClicked on the result of the dynamic_cast, not the result of getObject.
This line has several problems:
if (dynamic_cast<SButton*>(SSceneManager::getGUIManager().getObject("testbutton").isClicked()))
First SSceneManager::getGUIManager().getObject("testbutton") return a unique_ptr reference. And as the compiler said, unique_ptr does not hae an isclicked method. For that, you would need to use the -> operator which is overloaded to return the underlying pointer.
Second, even if it worked, you can not dynamic_cast a bool to a pointer.
You could do something like
if (dynamic_cast<SButton*>(SSceneManager::getGUIManager().getObject("testbutton").get())->isClicked) ...
Although you might want to separate it in 2 lines to make sure dynamic_cast does not give you a NULL pointer.
SBButton* button = dynamic_cast<SButton*>(SSceneManager::getGUIManager().getObject("testbutton").get());
if (button && button->isClicked()) ...