Return value in a get function - c++

This is a question to understand the correct way to implement a get function in the below scenario.
I got the below class:
class A
{
string m_sName;
public:
A(): m_sName ("this") {}
const string& getName() const
{
return m_sName;
}
};
The problem with the above implementation of the get function is, it allows the client to modify the data member. That's:
int main ()
{
A a;
const string& data_mem = a.getName();
string& s = const_cast<string&> (data_mem);
s += " Pointer";
cout << a.getName().c_str () << endl;
return 0;
}
The output would be: this Pointer
I know we can use reference parameter for get function or simply return string. But both will create new object.
Is there a better way to do this? Or prohibit const_cast (on a user defined class)?

C++ gives you the ability to shoot yourself in the foot.
Programmers should use const_cast with extreme caution: being aware that the behaviour on using it to cast away the const on an object that was initially declared as const is undefined.
Code analysis tools could help you.

You could avoid the problem completely by simply returning by value
string getName() const

Add another version of getName() that isn't marked as a const method and returns a non-const string&:
string& getName() { return m_sName; }
That will return a reference to the string in the instance of A, and won't create a copy. It will however amend the string in the class.
I should point out this is the wrong way to do things in OO terms, both in terms of encapsulation and data hiding. Your class's abstraction for how the name is stored is leaking to the outside world. The correct approach would be to add an UpdateName or AppendToName method.

Related

How to call overloaded function based on constness and return type?

A rookie question here. I have noticed something similar with what I am trying to achieve in the cryptic vector class:
iterator begin();
const_iterator begin() const;
I have tried to achieve this with the following implementation:
#include <iostream>
class User
{
public:
class A
{
public:
A(){ std::cout << "A constructor" << std::endl; }
};
class B
{
public:
B(){ std::cout << "B constructor" << std::endl;}
};
public:
A begin() { return A(); }
B begin() const { return B(); }
};
int main()
{
User u;
User::A a = u.begin();
User::B b = u.begin();
}
With this code, I can call the A constructor yet cannot find a way how to call the B constructor.
I get the following error message:
no user-defined conversion from User::A to User::B
Which, I guess, is indicative that the wrong member function is called.
Any tips? :)
Before solving the problem, I'll add a small comment so that you understand why C++ behaves that way.
Basically, const functions are there so that you could expose different APIs when you are accessing mutable vs immutable object instances.
So, for example, you might have define an API that returns a list of const User-s that only allow calling specific getter methods, whereas a different API would return a single (non-const) User for editing.
The compiler automatically uses the right method according to the const-ness of the instance you are using.
So, if you have a const User a and User b, for user a, the default is going to be the const prototype, and for user b, the default is going to be the non-const one, and it falls back to the const one if none exists.
Note: As Goswin correctly pointed out, is that the compiler has no way of choosing a method by its return type, but rather only by examining its arguments. C++ methods, as in, functions attached to class instances, pass an "implicit" pointer to the class instance, called this. When defining a const method, you're basically telling the compiler to pass a const pointer as opposed to a regular one for non-const ones.
Just like others pointed out:
If you cast your user into a const one, you can call the const prototype.
OR, if you define or return a const User variable, you'll also access the const prototype as well.
There are different ways to cast:
C way (const User&)u (which you should avoid, but be able to recognize)
Classic const_cast<const User&>(u)
C++17 std::as_const(u)
And you can always return a const reference to an object from a method if you wanna expose only the const prototypes.
You need const User, std::as_const (C++17) might help:
User::B b = std::as_const(u).begin();
The problem is that the second overloaded begin is a const member function which means that when you write u.begin() the first non-const version will be preferred over the second const version since u is a nonconst User object.
To solve this one option is to use const_cast as shown below:
User::B b = const_cast<const User&>(u).begin();
Demo

Can I delete function for an rvalue version of object?

For legacy reasons, there's a lot of usage of const char* in the code I'm working on. I am trying to limit that, and stumbled on something I'd like to know. I have something like:
class AClass {
public:
const char* getValue() { return _value.c_str(); }
private:
std::string _value;
}
But this class now may be returned by copy, eg. by function:
AClass getAClass();
We also might wanna pass that to something like this:
void functionFromOtherLibrary(const char* value);
Now thinking about that, this might cause an error:
functionFromOtherLibrary(getAClass().getValue());
since the intermediate is eligible to be destroyed at that point. Even if the above goes OK, because it's one statement, this probably won't:
const char* str = getAClass().getValue();
functionFromOtherLibrary(str);
So I was thinking of writing something like:
class AClass {
public:
const char* getValue() { return _value.c_str(); }
const char* getValue() && = delete;
}
to forbid calls for that method on rvalues. Just trying that gave me:
error C2560: cannot overload a member function with ref-qualifier with a member function without ref-qualifier
I'm not sure if this:
is valid construct and
is ever necessary. I've seen a lot of code that returns const char*s and it always seems to rely on the fact that the object returning the value will still exist and hold the source std::string.
I would really appreciate a more detailed explanation what happens when code uses std::strings to hold strings but only communicates with C strings.
And if you want to suggest removing C strings - that's what I'm trying to do right now. I still want an answer though.
You can't overload a function with a ref-qualifier with a function without a ref-qualifier. The MSVC error text is nice and clear on this point.
But you can just add the ref-qualifier to the other one:
class AClass {
public:
const char* getValue() & { return _value.c_str(); }
const char* getValue() && = delete;
};
Whether this is the right design or not is a separate question - but if you decide that it is, this is how you would do it.

Overloading operator << , os gets a string

so I have problem with my code, I want to overload operator <<, all the functions are in abstract class Employee so
friend std::ostream &operator<<(std::ostream &os, const Employee &employee) {
os<<employee.print();
return os;
}
That's a function print:
virtual const std::string& print() const {
return "description: "+this->description+ " id: "+ std::to_string(this->getID()); }
Description and ID just a variables in class Employee
And it just doesn't work and I get exception E0317, I understand it like what print returns it's not a string.
Also, if I change return type to
std::basic_string<char, std::char_traits<char>, std::allocator<char>>
it magically works, but I don't understand why I can't use standard string.
const std::string& print() const
This returns a reference to a temporary string, which goes out of scope as soon as it's created and the reference you use outside the function is therefore invalid.
To make it work in the situation you currently use the function, you need to change it to:
const std::string print() const
An even better solution would be to also drop const on the return value since making changes to the returned std::string can not affect the Employee object. There's no reason to try to restrict future users of the print() function if they want to std::move the returned string or make changes to it in other ways.
So, this would be a better signature:
std::string print() const
As formerlyknownas_463035818 implied in a comment, this function doesn't really have anything to do with printing. It returns a string representation of the object so to_string would indeed be a more appropriate name.

How to declare an accessor to a member?

In some of the classes accessors are declared like getName1 and in others like getName2. From the usage perspective, it looks identical. Are there any performance benefit of one over the other in a decent compiler? Are there any cases where I should use only one of the two?
class MyClass
{
public:
MyClass(const string& name_):_name(name_)
{
}
string getName1() const
{
return _name;
}
const string& getName2() const
{
return _name;
}
private:
string _name;
};
int main()
{
MyClass c("Bala");
string s1 = c.getName1();
const string& s2 = c.getName1();
string s3 = c.getName2();
const string& s4 = c.getName2();
return 0;
}
Returning by reference is potentially faster, because no copy needs to be made (although in many circumstances the return-value optimization applies.
However, it also increases coupling. Consider what happens if you want to change the internal implementation of your class so that you store the name in a different way (i.e. no longer in a string). There will no longer be something to return by reference, so you will also need to change your public interface, which means client code will need to be recompiled.
string getName1() const
{
return _name;
}
The compiler can seriously do RVO(return value optimization) in this case.
This article from Dave Abrahams may help.

overloading operator .

Ok, I know that operator. is not overloadable but I need something like that.
I have a class MyClass that indirectly embeds (has a) a std::string and need to behave exactly like a std::string but unfortunately cannot extend std::string.
Any idea how to achieve that ?
Edit: I want that the lines below to compile fine
MyClass strHello1("Hello word");
std::string strValue = strHello1;
const char* charValue = strHello1.c_str();
As per your later edit, that:
MyClass strHello1("Hello word");
std::string strValue = strHello1;
const charValue = strHello1.c_str();
should compile fine, the only solution is to write a wrapper over std::string:
class MyClass
{
private:
std::string _str;
public:
MyClass(const char*);
//... all other constructors of string
operator std::string& ();
//... all other operators of string
const char* c_str();
//... all other methods of string
friend std::string& operator + ( std::string str, Foo f);
};
//+ operator, you can add 2 strings
std::string& operator + ( std::string str, Foo f);
This however is tedious work and must be tested thoroughly. Best of luck!
You can overload the conversion operator to implicitly convert it to a std::string:
class MyClass
{
std::string m_string;
public:
operator std::string& ()
{
return m_string;
}
};
If you're not interested in polymorphic deletion of a string*, you can derived from std::string, just getting the behavior of your class behaving "as-a" std::string. No more, no less.
The "you cannot derived from classes that don't have a virtual destructor" litany (if that's the reason you say you cannot extend it) it like the "dont use goto", "dont'use do-while" "don't multiple return" "don't use this or that feature".
All good recommendation from and for people that don't know what they are doing.
std::string doesn't have a virtual destructor so don't assign new yourcalss to a std::string*. That's it.
Just make your class not having a virtual destructor itself and leave in peace.
Embedding a string and rewriting all its interface just to avoid inheritance is simply stupid.
Like it is stupid writing dozens of nested if to avoid a multiple return, just as its stupid introduce dozens of state flags to avoid a goto or a break.
There are cases where the commonly considered weird things must be used. That's why those things exist.
You can extend an interface without publically deriving from it. This prevents the case of problematic polymorphic destruction (aka no virtual destructor):
#include <iostream>
#include <string>
using namespace std;
class MyClass
: private std::string
{
public:
MyClass(const char* in)
: std::string(in)
{}
// expose any parts of the interface you need
// as publically accessible
using std::string::c_str;
using std::string::operator+=;
};
int main()
{
MyClass c("Hello World");
cout << c.c_str() << "\n";
c += "!";
cout << c.c_str() << "\n";
// Using private inheritance prevents casting, so
// it's not possible (easily, that is) to get a
// base class pointer
// MyClass* c2 = new MyClass("Doesn't work");
// this next line will give a compile error
// std::string* pstr = static_cast<MyClass*>(c2);
// delete c2;
}
Trying to sum-up all the discussions, it looks like you will never find a suitable dolution because ... your requirements are in cotraddiction.
You want you class to behave as ats::string
std::string behave as a value but
a value does not "change" when accessed but (think to c = a+b: do you expect a and b to change their value ??) ...
when accessing your "value" you want to change it.
If what I summed up collecting all the peaces (suggestion: edit your question otherwise all the answerer will risk to be lost) is correct you are in trouble with 3 & 4 (note the 3. derives from the concept of "assignment", and you can do noting to change it, while 4. comes from you directly)
Now, I can try to reverse-engineer your psychology (because you didn't provide good information about what your class represent and why you cannot "extend" a string) I can find
two possibility neither of which will fit all the above requirements.
Store a string into your class and make your class to behave as a std::string. There are threee ways to come to this point:
a. embed a string and make your class to decay to it:
essentially your class must contain
.
operator std::string&() { return my_string; } //will allow l-value assignments
operator const std::string&() const { return my_string; } //will allow r-value usage
const char* c_str() const { return my_string.c_str(); }
b. derive from std::string. In fact that's like having an anonymous my_string in it, which "decay" operations implicit. Not that different as above.
c. embed or privately derive, and rewrite the std::string interface delegating the std::string functions. It's just a long typing to get exactly the b. result. so what the f..k? (this last question is dedicated to all the ones that believe a. or b. will break encapsulation. c. will break it as well, it will only take longer!)
Don't store a string, but just get it as a result from a calculation (the "lookup" you talk about, not clarifying what it is).
In this last case, what you need is a class that decay automatically into std::string as a value.
So you need, in your class
operator std::string() const { return your_lookup_here; }
note that const is necessary, but there is no need to modify your class inner state, since the resulting string is not stored.
But this last solution has a problem with const char*: since the string is temporary, until you don't assign it, its buffer is temporary as well (will be destroyed) so decaying into const char* to assign the pointer is clueless (the pointer will point to a dead buffer).
You can have a
const char* c_str() const { return std::string(*this).c_str(); } //will call the previous one
but you can use this only into expressions or a pass-through parameter in function calls (since the temporary will exist until the evaluating expression is fully evaluated), not in an assignment towards an l-value (like const char* p; p = myclassinstace.c_str(); )
In all of the above cases (all 1.) you also need:
myclass() {}
myclass(const std::string& s) :my_string(s) { ... }
myclass(const char* s) :my_string(s) { ... }
or - in C++11, just
template<class ...Args>
myclass(Args&&... args) :my_string(std::forward<Args...>(args...)) {}
In case 2., instead of initialize the not existent my_sting, you should use the arguments to set what you will look up (we don't know what it is, since you did not tell us)
Hope in all these option you can find something useful.
You can add an operator const reference to string, as follows:
class C
{
public:
// Implicit conversion to const reference to string (not advised)
operator const std::string &() const
{
return s_;
}
// Explicit conversion to a const reference to string (much better)
const std::string &AsString() const
{
return s_;
}
private:
std::string s_;
};
As you can see from the comments, it would be much better to add an explicit conversion function if you really need this behavior. You can read about why this is bad in the Programming in C++ Rules and Recommendations article.