const int' vs. 'int const' as function parameters in C++ and C
const T
and T const
are identical. With pointer types it becomes more complicated:
const char*
is a pointer to a constantchar
char const*
is a pointer to a constantchar
char* const
is a constant pointer to a (mutable)char
In other words, (1) and (2) are identical. The only way of making the pointer (rather than the pointee) const
is to use a suffix-const
.
This is why many people prefer to always put const
to the right side of the type (“East const” style): it makes its location relative to the type consistent and easy to remember (it also anecdotally seems to make it easier to teach to beginners).
What is the difference between const int*, const int * const, and int const *?
Read it backwards (as driven by Clockwise/Spiral Rule):
int*
- pointer to intint const *
- pointer to const intint * const
- const pointer to intint const * const
- const pointer to const int
Now the first const
can be on either side of the type so:
const int *
==int const *
const int * const
==int const * const
If you want to go really crazy you can do things like this:
int **
- pointer to pointer to intint ** const
- a const pointer to a pointer to an intint * const *
- a pointer to a const pointer to an intint const **
- a pointer to a pointer to a const intint * const * const
- a const pointer to a const pointer to an int- ...
And to make sure we are clear on the meaning of const
:
int a = 5, b = 10, c = 15;
const int* foo; // pointer to constant int.
foo = &a; // assignment to where foo points to.
/* dummy statement*/
*foo = 6; // the value of a can´t get changed through the pointer.
foo = &b; // the pointer foo can be changed.
int *const bar = &c; // constant pointer to int
// note, you actually need to set the pointer
// here because you can't change it later ;)
*bar = 16; // the value of c can be changed through the pointer.
/* dummy statement*/
bar = &a; // not possible because bar is a constant pointer.
foo
is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to a const char
. You may change which string you point to but you can't change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn't be changed.
bar
is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use a T* const
pointer unless you need to allow NULL
pointers.
What is the difference between int const function(parameters), int function(const parameters), and int function(parameters) const?
Considering you talking about c++
:
const int function(parameters) // instead of your int const function(parameters)
will return a constant to the int
int function(const parameters)
The parameters will be considered as constant inside the method, which means they will not be changed.
int function(parameters) const
This function will not change any class variable (if the member is not mutable)
C - Which is the advantage for the user of const in parameters of functions that are not pointers?
Some people here say that you should write const
if you won't change the variable - as you can declare any local variable const
. (The main goal is to keep you from accidently changing the variable, but also may help the compiler to optimize your code)
Other people say that you shouldn't declare function parameter const
, because it will expose some of the implementation in the API.
I think that they both are right! That is why you can omit the const
in function declaration: You can write void func(int);
in a header, but implement it void func(const int i) {}
in the code file.
const int *p vs. int const *p - Is const after the type acceptable?
The most important thing is consistency. If there aren't any coding guidelines for this, then pick one and stick with it. But, if your team already has a de facto standard, don't change it!
That said, I think by far the more common is
const int * i;
int * const j;
because most people write
const int n;
instead of
int const n;
A side note -- an easy way to read pointer const
ness is to read the declaration starting at the right.
const int * i; // pointer to an int that is const
int * const j; // constant pointer to a (non-const) int
int const * aLessPopularWay; // pointer to a const int
Difference of function argument as (const int &) and (int & a) in C++
You should use const
in the signature whenever you do not need to write. Adding const
to the signature has two effects: it tells the compiler that you want it to check and guarantee that you do not change that argument inside your function. The second effect is that enables external code to use your function passing objects that are themselves constant (and temporaries), enabling more uses of the same function.
At the same time, the const
keyword is an important part of the documentation of your function/method: the function signature is explicitly saying what you intend to do with the argument, and whether it is safe to pass an object that is part of another object's invariants into your function: you are being explicit in that you will not mess with their object.
Using const
forces a more strict set of requirements in your code (the function): you cannot modify the object, but at the same time is less restrictive in your callers, making your code more reusable.
void printr( int & i ) { std::cout << i << std::endl; }
void printcr( const int & i ) { std::cout << i << std::endl; }
int main() {
int x = 10;
const int y = 15;
printr( x );
//printr( y ); // passing y as non-const reference discards qualifiers
//printr( 5 ); // cannot bind a non-const reference to a temporary
printcr( x ); printcr( y ); printcr( 5 ); // all valid
}
int vs const int&, And when I use it
You should generally use references if you plan on continuing to use the value you are passing into a function after that function completes. As you have highlighted, sometimes it is more costly to pass a reference. This can happen if your data is very small (you normally wouldn't bother passing integers as arguments), or if you have an opportunity to move
the contents of the variable (though this can be a more complicated topic).
Using references allows you to avoid copying data while still guaranteeing that the argument is valid, since unlike with pointers there is no null reference. References give you the opportunity of allowing the function to mutate the contents of your variable, or with const references you can prevent a function from modifying the variable you pass in. In general you should prefer to use references over pointers when you don't need features only available with a pointer, and prefer const references over references when you don't need mutation.
int vs const int&
In C++ it's very common what I consider an anti-pattern that uses const T&
like a smart way of just saying T
when dealing with parameters. However a value and a reference (no matter if const or not) are two completely different things and always and blindly using references instead of values can lead to subtle bugs.
The reason is that when dealing with references you must consider two issues that are not present with values: lifetime and aliasing.
Just as an example one place where this anti-pattern is applied is the standard library itself, where std::vector<T>::push_back
accepts as parameter a const T&
instead of a value and this can bite back for example in code like:
std::vector<T> v;
...
if (v.size())
v.push_back(v[0]); // Add first element also as last element
This code is a ticking bomb because std::vector::push_back
wants a const reference but doing the push_back may require a reallocation and if that happens means that after the reallocation the reference received would not be valid any more (lifetime issue) and you enter the Undefined Behavior realm¹.
Much better from a logical point of view in today C++ would be to accept a value (i.e. void std::vector<T>::push_back(T x)
) and then efficiently moving that value in the final place inside the container. Then the caller may eventually use std::move
if that is deemed important (note however that the idea of moving construction was not present in original C++).
Aliasing issues are instead a source of subtle problems if const references are used instead of values. I've been bitten for example by code of this kind:
struct P2d
{
double x, y;
P2d(double x, double y) : x(x), y(y) {}
P2d& operator+=(const P2d& p) { x+=p.x; y+=p.y; return *this; }
P2d& operator-=(const P2d& p) { x-=p.x; y-=p.y; return *this; }
};
struct Rect
{
P2d tl, br;
Rect(const P2d& tl, const P2d& br) : tl(tl), bt(br) {}
Rect& operator+=(const P2d& p) { tl+=p; br+=p; return *this; }
Rect& operator-=(const P2d& p) { tl-=p; br-=p; return *this; }
};
The code seems at a first glance pretty safe, P2d
is a bidimensional point, Rect
is a rectangle and adding/subtracting a point means translating the rectangle.
If however to translate the rectangle back in the origin you write myrect -= myrect.tl;
the code will not work because the translation operator has been defined accepting a reference that (in that case) is referencing a member of same instance.
This means that after updating the topleft with tl -= p;
the topleft will be (0, 0)
as it should but also p
will become at the same time (0, 0)
because p
is just a reference to the top-left member and so the update of bottom-right corner will not work because it will translate it by (0, 0)
hence doing basically nothing.
Please don't be fooled into thinking that a const reference is like a value because of the word const
. That word exists only to give you compile errors if you try to change the referenced object using that reference, but doesn't mean that the referenced object is constant. More specifically the object referenced by a const ref can change (e.g. because of aliasing) and can even get out of existence while you are using it (lifetime issue).
In const T&
the word const expresses a property of the reference, not of the referenced object: it's the property that makes impossible to use it to change the object. Probably readonly would have been a better name as const has IMO the psychological effect of pushing the idea that the object is going to be constant while you use the reference.
You can of course get impressive speedups by using references instead of copying the values, especially for big classes. But you should always think about aliasing and lifetime issues when using references because under the cover they're just pointers to other data.
For "native" data types (ints, doubles, pointers) references however are actually going to be slower than values and there's nothing to gain in using them instead of values.
Also a const reference will always mean problems for the optimizer as the compiler is forced to be paranoid and every time any unknown code is executed it must assume that all referenced objects may have now a different value (const
for a reference means absolutely NOTHING for the optimizer; that word is there only to help programmers - I'm personally not so sure it's such a big help, but that's another story).
(1) Apparently (https://stackoverflow.com/a/18794634/320726) the standard says that this case is valid but even with this interpretation (on which I do not agree at all) still the problem is present in general. push_back
doesn't care about the identity of the object and so should have taken the argument by value. When you pass a const reference as value to a function it's your responsibility to ensure that the referenced object will stay alive for the full duration of the function. With v.push_back(v[0])
this is simply false if no reservation was done and IMO (given the push_back
signature) is a caller's fault if that happens. The real logic bug is however the push_back
interface design (done intentionally, sacrificing logical correctness on the altar of efficiency). Not sure if it was because of that defect report but I saw a few compilers "fixing" the problem in this special case (i.e. push_back
does a check to see if the element being pushed is coming from the vector itself).
What is the difference between static const int and static int const?
The grammar for declaration specifiers is given in C 2018 6.7 1, and it shows that specifiers for storage class (such as static
), type (such as short
or double
), qualifiers (such as const
), functions (inline
and _Noreturn
), and alignment may appear in any order. Nothing in clause 6.7 gives any meaning to the order in which the specifiers appear, so we may presume any combination of specifiers has the same meaning regardless of order.
The only mention of “order” in this regard appears in 6.7.2 2, which says “… the type specifiers may occur in any order, possibly intermixed with the other declaration specifiers.” So you can write long static int const long
for static const long long int
, just as you can say “square red big house” instead of “big square red house”—there is no rule against it, but it will seem funny to people and may throw them off.
Note that the *
that indicates a pointer, as well as (
and )
for either grouping or argument lists and [
and ]
for subscripts are not declaration specifiers and may not be freely reordered with declaration specifiers. (They are in fact part of a declarator, which is a separate part of a declaration from the declaration-specifiers.)
However, the standard describes using storage-class specifiers after other specifiers or qualifiers as obsolescent, in 6.11.5:
The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature.
“Obsolescent” means the feature may be considered for withdrawal in future revisions of the standard (per Introduction paragraph 2). Thus, compilers that issue a warning for using const static
are suggesting a change that helps prepare the source code for a future version of C.
Use of 'const' for function parameters
The reason is that const
for the parameter only applies locally within the function, since it is working on a copy of the data. This means the function signature is really the same anyways. It's probably bad style to do this a lot though.
I personally tend to not use const
except for reference and pointer parameters. For copied objects it doesn't really matter, although it can be safer as it signals intent within the function. It's really a judgement call. I do tend to use const_iterator
though when looping on something and I don't intend on modifying it, so I guess to each his own, as long as const
correctness for reference types is rigorously maintained.
Related Topics
Why Does C++ Output Negative Numbers When Using Modulo
How to Do an Integer Log2() in C++
Exporting Classes Containing 'Std::' Objects (Vector, Map etc.) from a Dll
How to Print an Unsigned Char as Hex in C++ Using Ostream
Demote Boost::Function to a Plain Function Pointer
Resolution of Std::Chrono::High_Resolution_Clock Doesn't Correspond to Measurements
High Resolution Timer With C++ and Linux
C++ Dll Export: Decorated/Mangled Names
Trailing Return Type Using Decltype With a Variadic Template Function
Linux: Executing Child Process With Piped Stdin/Stdout
Std::Thread Pass by Reference Calls Copy Constructor
C++11 Allows In-Class Initialization of Non-Static and Non-Const Members. What Changed
What's Your Favorite Profiling Tool (For C++)
Multi Line Preprocessor Macros