Const Keyword in Function [duplicate]
In Effective C++
Item 03, Use const whenever possible.
class Bigint
{
int _data[MAXLEN];
//...
public:
int& operator[](const int index) { return _data[index]; }
const int operator[](const int index) const { return _data[index]; }
//...
};
const int operator[]
does make difference from int& operator[]
.
But what about:
int foo() { }
and
const int foo() { }
Seems like that they're the same.
My question is, why we use const int operator[](const int index) const
instead of int operator[](const int index) const
?
Solution 1:
Top level cv-qualifiers on return types of non class type are ignored. Which means that even if you write:
int const foo();
The return type is int
. If the return type is a reference, of course,
the const
is no longer top level, and the distinction between:
int& operator[]( int index );
and
int const& operator[]( int index ) const;
is significant. (Note too that in function declarations, like the above, any top level cv-qualifiers are also ignored.)
The distinction is also relevant for return values of class type: if you
return T const
, then the caller cannot call non-const functions on the
returned value, e.g.:
class Test
{
public:
void f();
void g() const;
};
Test ff();
Test const gg();
ff().f(); // legal
ff().g(); // legal
gg().f(); // **illegal**
gg().g(); // legal
Solution 2:
You should clearly distinguish between the const usage applying to return values, parameters and the function itself.
Return values
- If the function returns by value, the const doesn't matter, as the copy of the object is being returned. It will however matter in C++11 with move-semantics involved.
- It also never does matter for basic types, as they are always copied.
Consider const std::string SomeMethod() const
. It won't allow the (std::string&&)
function to be used, as it expects non-const rvalue. In other words, the returned string will always be copied.
- If the function returns by reference,
const
protects the returned object from being modified.
Parameters
- If you pass a parameter by value, the const prevents the modification of given value by function in function. The original data from parameter can't be modified anyway, as you only have copy.
- Note that since the copy is always created, the const has only meaning for function body, thus, it's checked only in function definition, not in declaration(interface).
- If you pass a parameter by reference, the same rule as in return values applies
Function itself
- If the function has
const
at the end, it can only run otherconst
functions, and can't modify or allow modification of class data. Thus, if it returns by reference, the reference returned must be const. Only const functions can be called on object or reference to object which is const itself. Also the mutable fields can be changed. - The behavior created by the compiler changes
this
reference toT const*
. The function can alwaysconst_cast
this
, but of course this shouldn't be done and is considered unsafe. - It's of course sensible to only use this specifier on class methods; global functions with const at the end will raise compilation error.
Conclusion
If your method doesn't and never will modify the class variables, mark it as const and be sure to meet all the critieria needed. It will allow more cleaner code to be written, thus keeping it const-correct. However, putting const
everywhere without giving it any thought certainly isn't the way to go.
Solution 3:
There is little value in adding const
qualifications to non-reference/non-pointer rvalues, and no point in adding it to built-ins.
In the case of user-defined types, a const
qualification will prevent callers from invoking a non-const
member function on the returned object. For example, given
const std::string foo();
std::string bar();
then
foo().resize(42);
would be forbidden, while
bar().resize(4711);
would be allowed.
For built-ins like int
, this makes no sense at all, because such rvalues cannot be modified anyway.
(I do remember Effective C++ discussing making the return type of operator=()
a const
reference, though, and this is something to consider.)
Edit:
It seems that Scott did indeed give that advice. If so, then due to the reasons given above, I find it questionable even for C++98 and C++03. For C++11, I consider it plainly wrong, as Scott himself seems to have discovered. In the errata for Effective C++, 3rd ed., he writes (or quotes others who complained):
The text implies that all by-value returns should be const, but cases where non-const by-value returns are good design are not difficult to find, e.g., return types of std::vector where callers will use swap with an empty vector to "grab" the return value contents without copying them.
And later:
Declaring by-value function return values const will prevent their being bound to rvalue references in C++0x. Because rvalue references are designed to help improve the efficiency of C++ code, it's important to take into account the interaction of const return values and the initialization of rvalue references when specifying function signatures.