Const Int' Vs. 'Int Const' as Function Parameters in C++ and C

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:

  1. const char* is a pointer to a constant char
  2. char const* is a pointer to a constant char
  3. 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 int
  • int const * - pointer to const int
  • int * const - const pointer to int
  • int 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 int
  • int ** const - a const pointer to a pointer to an int
  • int * const * - a pointer to a const pointer to an int
  • int const ** - a pointer to a pointer to a const int
  • int * 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 constness 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



Leave a reply



Submit