0

I was writing a C++ function that takes int** as input and the function itself does not modify the data (i.e. matrix elements). So I decided to make the argument a const int** type (Is that an acceptable practice?). A sample code is like this:

void func1(const int* k) {
    std::cout << *k << '\n';
}
void func2(const int** k) {
    std::cout << **k << '\n';
}
int main() {
   int m = 0;
   int* p1 = &m;
   func1(p1);
   int** p2 = &p1;
   func2((const int**)p2); // compilation error unless explicit type conversion
   // func2(p2); /* [*] compilation error */
   (**p2) += 1;
   func1(p1);
   func2((const int**)p2);
   return 0; 
}

My question is, when calling func2, I have to explicitly convert the double-pointer p2 to const int**, otherwise it gives the following error:

 error: invalid conversion from ‘int**’ to ‘const int**’ [-fpermissive]

Whereas func1 has no such issue. I tested this with both g++ (10.2.0) on Cygwin and Intel's icpc. Could someone explain why the compiler behaves this way and what is a good practice in this situation?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • If this conversion were allowed without a cast, it would violate const correctness: it would allow one to produce an `int*` pointer pointing to a `const int` object. Proof is left as an exercise for the reader. – Igor Tandetnik Oct 09 '22 at 03:33
  • 1
    As to the best practice - it's usually best to not have pointers to pointers to begin with. See also: [three-star programmer](https://wiki.c2.com/?ThreeStarProgrammer) – Igor Tandetnik Oct 09 '22 at 03:36

2 Answers2

2

In a sense, having a const int ** is saying "this pointer points to a pointer to an integer that won't be modified". The problem with implicitly casting an int ** to const int ** is that that promise is easy to mistakenly not keep. For example:

const int c = 1;
int *p1;
const int **p2 = &p1;   /* implicit cast from int** to const int** */
*p2 = &c;               /* change what the pointed-to pointer points to */
*p1 = 2;                /* change a value pointed to by a const int** */

So the requirement for an explicit cast is to make you think about this, I guess.

Example adapted from here.

defab67
  • 21
  • 4
0

With multiple levels of pointers, a type is only more const-qualified if every level of pointer from the right has const added. This is for const-correctness reasons so that when you assign to the pointer, you can't accidentally assign a more const-qualified pointer at the "lower" levels of pointers when you have multiple pointers.

For example:

int ***     *     *     * i;
int ***const*const*const* j = i;
    // ^ All levels after this have to be const-qualified

This includes the left-most type, const int. So, if your data is not modified, you should have int const* const* k.

This is the case in C++ (as a qualification conversion), but this is still allowed implicitly in C because all pointers can be implicitly converted.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • Wouldn't that be `int * const *k`? But yes, you are correct, you have to specify `const` for each level you want to preserve const-correctness for. – David C. Rankin Oct 09 '22 at 03:58
  • Thanks! I think `int * const *k` does not make the `int` type data constant while we need to make every level constant (`int` and `int*`). @DavidC.Rankin Also another takeway is that multiple levels of pointers may shoot myself in the foot :) – oaaij-gnahz Oct 09 '22 at 04:11
  • Amen. To have both levels of indirection `const` you would need `const int * const *k` which would make `k` a constant pointer to constant int pointer (clockwise-spiral-rule). That's enough to wrap anyone around the axle. – David C. Rankin Oct 09 '22 at 04:21
  • @oaaij-gnahz I wrote it as `int const * const * k` just so it's more clear from the multiple level of `const`s. this is equivalent to `const int * const * k`, which is what you want – Artyer Oct 09 '22 at 12:09