views:

177

answers:

6

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?

A: 

I've found this to be a helpful guide

Jeff Barger
-1 for the link to C++ FQA Lite.
In silico
A: 

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?
André Caron
`const char *` and `char const *` are modifiable pointers to unmodifiable characters. `char * const` is an unmodifiable pointer to modifiable characters. `const` applies to whatever comes immediately before it or, if it is first, whatever comes immediately after it.
Zooba
The `const` after parentheses is NOT redundant: it applies to the "*this" object, not the return type.
aschepler
Oops, was a bit too fast on that one. I'll edit it.
André Caron
+1  A: 

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

MykC
This is what cleared all my confusion about const.
PigBen
+2  A: 

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 deleteing 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.

Zooba
A: 

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;
aschepler
A: 

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.

ArunSaha
But they have different semantics. From my point of view, the only ones that make sense (in most circumstances) are B and D. You might also consider `string ToString() const;`. B is only useful if you want to let the user modify the string, of course. D is preferred if you are just going to return a reference to a string member of `MyClass`. If the string is not a member (i.e. computed during the function call), use the form above. One possible use for C is if MyClass has to compute the string, but caches it as a member variable (and hence is not const). However, then you should use `mutable`
Matthew Hall
Yeah, I would also avoid option C in almost all circumstances. D alone makes sense. Defining _both_ B and D makes sense if you want version B. And version A alone does make sense if the string is calculated at function call time rather than being a simple member.
aschepler