Instead of this ¹non-standard code:
#import <string>
#import <iostream>
void example(const std::string& x) {
size_t length = x.length();
for (size_t i = 0; i < length; ++i) {
std::cout << x.at(i) << std::endl;
}
}
int main() {
example("hello");
}
… I'd write this:
#include <string>
#include <iostream>
using namespace std;
void example( string const& s )
{
for( char const ch : s )
{
cout << ch << '\n';
}
}
auto main()
-> int
{ example( "hello" ); }
The main place I could add const
, relative to the original code, was for the ch
variable in the loop. I think that's nice. const
is generally desirable because it reduces the possible code actions one has to consider, and range based loops let you have more const
.
The main drawback of using const
for most things, is when you have to relate to C APIs.
Then one just has to make some gut feeling decisions about whether to copy data, or trust in the documentation and use a const_cast
.
Addendum 1:
Do note that const
on a return type prevents move semantics. As far as I know this was first noted by Andrei Alexandrescu in his Mojo (C++03 move semantics) article in Dr Dobbs Journal:
” [A] const
temporary looks like an oxymoron, a contradiction in terms. Seen from a practical perspective, const
temporaries force copying at destination.
So, this is one place where one should not use const
.
Sorry that I forgot to mention this originally; I was reminded by user bogdan's comment on another answer.
Addendum 2:
In the same vein (to support move semantics), if the last thing done with a formal argument is to store a copy somewhere, then instead of passing by reference to const
it can be better to use a non-const
argument passed by value, because it can be simply moved from.
I.e., instead of
string stored_value;
void foo( string const& s )
{
some_action( s );
stored_value = s;
}
… or the redundancy of optimized
string stored_value;
void foo( string const& s )
{
some_action( s );
stored_value = s;
}
void foo( string&& s )
{
some_action( s );
stored_value = move( s );
}
… consider just writing
string stored_value;
void foo( string s )
{
some_action( s );
stored_value = move( s );
}
It can be slightly less efficient for the case of lvalue actual argument, it discards the advantages of const
(constraints on what the code could possibly do), and it breaks a uniform convention of using const
wherever possible, but it doesn't perform badly in any situation (which is the main goal, to avoid that) and it's smaller and possibly more clear code.
Notes:
¹ Standard C++ does not have an #import
directive. Also, those headers, when properly included, are not guaranteed to define size_t
in the global namespace.