Reputation: 1508
I thought the concept of "const-correctness" was pretty well defined, but when I talked to other people about it, it seemed we had different ideas of what it means. Some people say it's about a program having the "const" annotations in as many places as possible. Others define a program as const-correct if and only if there are no violations of constness where the const annotation is used (ie. it's a property that the compiler checks for you).
So I'd like to know, which of these functions are const correct:
struct Person {
string getName1() const { return _name; }
string getName2() { return _name; }
string getName3() const { _name = "John"; return _name; }
string getName4() { _name = "John"; return _name; }
string _name;
};
I've searched on the Internet for definitions but couldn't really find definite answer, and I also have suspicions that there might be a case of citogenesis at play. So could anyone provide any solid citation for a definition?
Upvotes: 8
Views: 3425
Reputation: 88801
In my view "const correctness" means:
Everything that isn't intended to be modified should be marked as
const
.
This means that the compiler can tell you when you make a mistake. (It also possibly has implications for optimisations in certain conditions, but that's more of a secondary benefit and depends on a lot of other factors)
There are three basic places where this is useful:
Member functions can be marked const
, as in your examples. This means that your accesses to member variables from within that function will be as though the variables themselves were const
. (This is the example you've shown, although getName3()
won't work)
"Free" variables, local and member variables themselves may also be marked const
- once initialised they may not be changed. Example - local variable:
int f(int i) {
const int square = i*i;
// do something here that uses, but doesn't change square
return square;
}
Or free variable:
extern const double PI; // set somewhere else but not changed.
Or member variable:
class foo {
const int state; // can't be changed once constructed
foo(int s) : state(i) {}
};
Function arguments can also be marked const
, the definition can match a non-const declaration still:
void f(int i);
void f(const int i) {
// the fact that i is const is an implementation detail of f here
}
As a side note const
correctness for references is required in some cases:
void g(const std::string& s);
void f(std::string& s);
Of these two one can be used in more places:
g("hi"); // Fine
f("hi"); // Not fine - you can't bind a temporary to a reference
Of course if you meant to change s
then passing a temporary in makes little sense anyway.
Upvotes: 11
Reputation: 477600
Only getName3
violates const-correctness, because _name
is constant (specifically, it is const std::string &
) in the function body, and std::string::operator=
is not const
.
getName2
is poor design because it requires a mutable context even though it doesn't perform any mutations, so it should be declared const
, as it would otherwise lead to surprising restrictions, i.e. you cannot call it through a const Person &
.
You might also add "volatile" and "rvalue" correctness into the mix, which are similar in spirit.
Const-correctness can be felt through its non-local effects. Typical examples are relational operators (operator<
and operator==
): If you fail to mark them as const
, many standard algorithms and containers won't work with your class, as they will usually apply the operators to constant element references. In that sense, not being "as const as possible" puts unnecessary restrictions on the use of your code. From the opposite view, the same applies if your own functions unnecessarily require a mutable reference to an object, as this will preclude you needlessly from using constant references and deprive you of compile-time checks that the operation you thought was non-mutating did in fact not mutate the object.
Upvotes: 1
Reputation: 64283
This faq chapter talks only about const correctness for c++. To quote it :
It means using the keyword const to prevent const objects from getting mutated.
About your member functions:
string getName1() const { return _name; }
This one const-correct. It just returns copy of a member variable.
string getName2() { return _name; }
This one is not const-correct, because it is not declared const member.
string getName3() const { _name = "John"; return _name; }
Not const-correct, because it modifies a member variable in const member function. It should produce a compilation error.
string getName4() { _name = "John"; return _name; }
It is const-correct, because it modifies a member variable.
Upvotes: 0
Reputation: 968
getName3() is incorrect, because it changes name. The others are correct.
Specifying a method constant is sometimes inhandy and sometimes required. When I have finished coding my projects, I check which methods can be made constant to improve readability of my code. While still coding, I don't specify methods constant a lot, because it is obstructing the progress of the work. For the same reason, I declare all members public until I have finished the coding and then I apply private and protected whenever applicable.
Upvotes: 0
Reputation: 7230
Const correctness is correctly identifying and specifying cases where methods are either mutable or immutable.
Anything that isn't mutable should be marked const.
Read more on wikipedia
Upvotes: 0
Reputation: 94653
Text from parashift:
It means using the keyword const to prevent const objects from getting mutated.
Upvotes: 4