C++: Const correctness and pointer arguments
Solution 1
You have it backwards:
const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.
The following const
is indeed unnecessary, and there's no reason to put it in a function declaration:
void someFunc1(int * const arg);
However, you might want to put it in the function implementation, for the same reason that you might want to declare a local variable (or anything else) const
- the implementation may be easier to follow when you know that certain things won't change. You can do that whether or not it's declared const
in any other declarations of the function.
Solution 2
Well it is not meant for the caller but for the code inside the someFunc1
. So that any code inside someFunc1
wont accidentally change it. like
void someFunc1(int *arg) {
int i = 9;
arg = &i; // here is the issue
int j = *arg;
}
Lets do some case study:
1) Just making the pointed value const
void someFunc1(const int * arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
}
2) Just making the pointer const
void someFunc1(int * const arg) {
int i = 9;
arg = &i; // <- compiler error as pointer is const
}
3) Right way to use const if variables involved can be const:
void someFunc1(const int * const arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
arg = &i; // <- compiler error as pointer is const
}
This should clear all doubts. So I already mentioned it is meant for the function code and not for the caller and you should use the most restrictive of the 3 cases i mentioned above.
EDIT:
- Even in declarations of functions its a good practice to declare
const
. This will not only increase readability but also the caller will be aware of the contract and has more confidence regarding immutability of arguments. (This is required bcoz you generally share your header files so caller might not have your implementation c/cpp file) - Even compiler can point out better if both declaration and definitions are in sync.
Solution 3
You've got your logic the wrong way round. You should read the type backwards, so const int *
is a pointer to a const int
and int * const
is a const
pointer to an int.
Example:
void foo() {
int a = 0;
int b = 0;
int * const ptrA = &a;
*ptrA = 1;
ptrA = &b; ///< Error
const int * ptrB = &a;
*ptrB = 1; ///< Error
ptrB = &b;
const int * const ptrC = &a;
*ptrC = 1; ///< Error
ptrC = &a; ///< Error
}
To elaborate and show why you would want your function parameter to be a const int *
you might want to indicate to the caller that they must pass in an int
because you as a function want to change the value. Consider this code for instance:
void someFunc1(const int * arg) {
// Can't change *arg in here
}
void someFunc2(int * arg) {
*arg = 5;
}
void foo() {
int a = 0;
someFunc1(&a);
someFunc2(&a);
const int b = 0;
someFunc1(&b);
someFunc2(&b); ///< *** Error here. Must pass in an int not a const int.
}
Solution 4
Yes, you are correct (ignoring the fact that you got them the wrong way around)- there is no sense in taking non-reference const
parameters. In addition, there is no sense in returning non-reference const
values.
Solution 5
You have it the wrong way:
const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.
Generally speaking its easier to reason about constness when writting that expression slightly different: const int*
is the same type as int const *
. In that notation the rules are much clearer, const
always applies to the type preceding it, therefore:
int const * intPtr1; // Declares a pointer to const int.
int * const intPtr2; // Declares a const pointer to int.
int const * * const * complexPtr; // A pointer to const pointer to pointer to const int
When the type is written with a leading const
, the const
is handled as if it was written after the first type, so const T*
becomes T const *
.
void someFunc2(int * arg);
Is therefore not redundant, since someFunc2
may change the contents of arg
, while someFunc1
may not. void someFunc3(int * const arg);
would be redundant (and ambigous) though
Related videos on Youtube
Ben
Updated on January 11, 2020Comments
-
Ben over 4 years
I understand that a const pointer can be declared a couple ways:
const int * intPtr1; // Declares a pointer that cannot be changed. int * const intPtr2; // Declares a pointer whose contents cannot be changed. // EDIT: THE ABOVE CLAIMS ARE INCORRECT, PLEASE READ THE ANSWERS.
But what about the same principles within the context of function arguments?
I would assume that the following is redundant:
void someFunc1(const int * arg); void someFunc2(int * arg);
Since someFunc 1 and 2 do a pass-by-value for the pointer itself, its impossible for someFunc1 to change the value of the original pointer, in a given call to the function. To illustrate:
int i = 5; int * iPtr = &i; someFunc1(iPtr); // The value of iPtr is copied in and thus cannot be changed by someFunc1.
If these are true, then there is no point in ever declaring a function with a 'const int * ptr' type arg, correct?
-
Alan Stokes over 12 yearsYou have your declarations the wrong way round. "const int *" is a changeable pointer to an unchangeable int. "int * const" is an unchangeable pointer to a changeable int.
-
Ben over 12 yearsEdited the question so that trigger happy googlers won't run off misinformed. I left the mistake in so that the answers still have that context though.
-
-
Xeo over 12 yearsNon-reference
const
return values are even harmful. -
ruakh over 12 yearsBut that doesn't explain why you'd write
void someFunc1(const int * arg);
. Note that predeclaringsomeFunc1
asvoid someFunc1(int * arg);
still lets you define it asvoid someFunc1(int * const arg) { ... }
if you want; theconst
-ness of the parameter variable itself doesn't affect the actual function signature. -
ruakh over 12 years@Xeo: Could you elaborate on that, please? I didn't know they had any effect at all!
-
James McNellis over 12 years@ruakh: See When does a const return type interfere with template instantiation? It's a rather minor issue.
-
Xeo over 12 years@ruakh: Move semantics for one.
T const&&
can not be moved-from, even though you know it's a temporary that will be destroyed soon. -
ruakh over 12 years@JamesMcNellis: Thank you! Interestingly, from the link there, it turns out that Herb Sutter takes the opposite stance from DeadMG and Xeo: he contends that when using return-by-value, you should use
const
, at least for non-builtin return types. -
ruakh over 12 years@Xeo: Ah, thanks. That also might explain why Herb Sutter's article disagrees: it may have been written before C++11's move semantics were established.
-
James McNellis over 12 years@ruakh: That article was written over a decade ago, so yes, well before move semantics were established.
-
Ben over 12 yearsNow I look like a fool...Thanks for the concise explanation and affirming that there is indeed an unnecessary const in one of those definitions!
-
havexz over 12 years@ruakh pls go through the updated ans. That should ans your concern.
-
ruakh over 12 yearsSorry, no, your updated answer still doesn't answer the question. (See Mike Seymour's answer, above, for the correct answer.)
-
havexz over 12 years"However, you might want to put it in the function implementation, for the same reason that you might want to declare a local variable (or anything else) const" = "I already mentioned it is meant for the function code and not for the caller "...nyways the point is clear thats the bottom line...
-
ruakh over 12 yearsI guess I just have a strange preference for answers that actually answer the question!
-
havexz over 12 years@ruakh :) well I too hv same taste as you. well from question it looks the guy knows
const
but wondering what it is doing in context of functions. So thats the ans is all about. Nyways i edited to reflect some more light. -
dynamic over 11 yearsis there any difference between
const int * intPtr1
andint const * intPtr1;
? -
Mike Seymour over 11 years@yes123: No difference at all.
const
qualifies the thing before it, unless it's at the beginning, in which case it qualifies the first thing. So in both cases, it qualifies theint
, not the pointer.int * const
would qualify the pointer. -
Kindred over 5 years@MikeSeymour: Wow that's so clear, I wonder there is a question asking about this? (I was finding the question can be solved by your comment.)
-
gkhaos over 3 yearsCommon trick: read any c-like declaration from right to left:
int const * intPtr1
-->intPtr1
is a pointer to a const (read-only) integer