Reputation: 38889
const correctness has me somewhat confused.
What rule of thumb do you use to decide when something should be const or not?
e.g. consider this example
class MyClass
{
string ToString(); // this one?
const string& ToString(); // or this?
const string& ToString() const; // or this?
char* ToString(); // What about this?
const char* ToString(); // or this?
const char* ToString() const; // or this?
const char const* ToString(); // Is this legal?
const char const* ToString() const; // how about this?
char const* ToString(); // or even this?
};
Const can get really confusing.
What would be the difference between all these ToString methods?
If I understand correctly, the first one returns a new string object that can be modified if need be. The second one returns a constant reference maybe it should be string const& ToString(). The third one is probably a nonsense because references are always constant is that correct?
Thought I'd throw the old char* versions in there for comparison as I do have methods that return object pointers and I'm not sure whether they should be const or not.
I guess I'm just trying to understand the limitations and benefits of const correctness and how to decide up front whether something should be const or not and how to correctly apply const since placing const in different places changes the meaning.
EDIT: also, how do I deal with that '... discards qualifiers'. What does that actually mean?
Upvotes: 5
Views: 960
Reputation: 20383
Some variations in the question is on returning string
versus char *
. That I think is unrelated to the const
discussion (let me know if it isn't); I am considering the string
return variations.
class MyClass
{
string ToString(); // A
string & ToString(); // B (I added this)
const string& ToString(); // C
const string& ToString() const; // D
};
My preferences are in this order: D, C, B, A.
In all the variations, const
are used in two ways:
return type specifier [this first const
in D]
This means the returned object cannot be used to modify the returned object. The sentence sounds funny, doesn't it? Well, there may be other ways of modifying an object and this const
cannot stop that. See this FAQ item.
class invariant [the second const
in D]
This means that the method does not mutate (modify) the class object.
I prefer D over anything else because D guarantees that the object on which the method is invoked won't be modified. If an objective can be achieved (i.e. method can be implemented) without modifying an object, thats a big win in terms of design. I use it whenever I can. But if the object has to be modified (i.e. there is no way the method can be implemented without modifying the object), then D is ruled out.
Among the remaining, both B and C are preferable over A because B and C returns a reference, which avoids the copy construction required in A.
Between B and C, C is preferable because it is returning a const
reference.
Upvotes: 0
Reputation: 45249
What rule of thumb do you use to decide when something should be const or not?
Use it everywhere you can. Then, don't use it when you nedd to modify the object or grant access to something that may modify the object (i.e. returning references or proxies to an internal state).
The third one is probably a nonsense because references are always constant is that correct?
No, that is not correct. A reference is an alias, not a variable. Therefore, you cannot change which variable a reference "points" to, like you can with a pointer. However, you may have a reference to a mutable object (std::string&
).
What would be the difference between all these ToString methods?
They all pretty much differ on memory management techniques, but at a high-level, they all do the same thing except for the following:
char* ToString();
That ont returns a pointer to a mutable array of chars (presumably internal state).
Note that the family of:
char const* ToString();
const char* ToString(); // or this?
const char const* ToString(); // Is this legal?
are all different ways to write the same thing. Native types are all const when returned by value whether you write it or not.
The following 2 are the preferred way (provided an extra const
at the end) of returning strings in C++:
string ToString(); // this one?
const string& ToString(); // or this?
Which one of the two you will use depends on where you get the value from. If the string is a data member, I suggest you go for the latter because it is usually faster, though not so much if your string implementation uses Copy-On-Write semantics. If you have to compute a value and return it, you have to use the former because you cannot return a reference to a local variable.
The following two are correct, but I still recommend you use std::string
const char* ToString() const; // or this?
const char const* ToString() const; // how about this?
Upvotes: -1
Reputation: 72271
You can't overload functions which have the same name and same argument but different return type. You probably knew that, but just making sure.
const
after the ()
parentheses means that the function will not change the object you call it on. Usually something called ToString
doesn't change the object, so in all cases you probably want a const method.
The difference between returning string
and returning const string&
is that the reference doesn't make and object copy and may be faster, but you can only do it if you already have a string
object (for example, as a private member of MyClass
). If not (say you need to piece a few bits of data together and then return that string), you'll need to return the string
by value.
Using string
objects is usually preferable to using C-style char*
pointers, but since you ask: The fourth one, char*
, would let other code change the characters within the returned string, which is probably a bad thing. const char*
, char const*
and const char const*
are all the same thing. char *const
is technically different, but it wouldn't do much as a return type for the same reason that returning a const int
doesn't matter much: the caller can just copy and then change the return value.
In short, the best forms would be, in order:
const string& ToString() const;
string ToString() const;
const char* ToString() const;
Upvotes: 0
Reputation: 11438
Where you use const
depends on the purpose of the function. As James suggests in his comment (which is worth putting as an answer), put const
anywhere you can:
If the function is intended to modify state within it's object instance, don't put const
at the end of the signature.
If the function is intended to modify one of it's reference or pointer parameters, don't put const
on the parameter.
If the variable referenced by a pointer or reference should be modified, don't put const
on the type (remember, const
applies to the part of the definition immediately prior).
If the returned reference/pointer references a variables that should not be changed by the received, do put const
on the type.
Answering the examples given in the question is impossible without knowing the purpose of the functions. My tendency would be to use string ToString() const
and char* ToString() const
, with very clear documentation on who is responsible for delete
ing the char*
.
As an extra note, const char*
and char const*
are identical (pointer to unmodifiable characters). char* const
, on the other hand, is an unmodifiable pointer to modifiable characters.
Upvotes: 2
Reputation: 188
I've always used this FAQ for these types of questions as a starting point. http://www.parashift.com/c++-faq-lite/const-correctness.html
Upvotes: 1