const variable syntax

Pages: 123
i just read a code with different declaration of const, e.g.:

1
2
int const* num1;
int const* const num2;

what's the meaning of those? and how could const written reversed like those..? how many ways const can be written, actually..?
Look at this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{

    const int arr[] = { 7, 8, 9 };  // an array of three constants

    int const * num1 = arr;        // a pointer to an array of constants
    int const * const num2 = arr;  // a const pointer to an array of constants

    ++ num1;     // Incrementing this poiner is allowed, because the pointer as such is not const.
    std::cout << *num1 << std::endl;

    // ++ num2;  // Incrementing this pointer is not allowed, because the pointer as such is
                 // a constant.

    int const * const * const constPtr_of_constPtr_of_constInt = &num2;  // This is valid, think about!
    std::cout << **constPtr_of_constPtr_of_constInt << std::endl;
}
Last edited on
You can write const int or int const. But you cannot exchange the order of between the *'s
The rule is that const refers to the thing to it's left (the thing before it).
Obviously if it's the first word then there's nothing to it's left.
Only in that special case does it refer to the thing to it's right.
Read the variable declaration from right to left. So
int const* const
is a constant pointer to constant int.
Last edited on
You left out the reference look-alike int * const

Lets play:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int main()
{
  int x = 7;
  x = 42;

  int const y = 6;
  y = 42; // error: assignment of read-only variable 'y'

  int * a = &x;
  *a = 42;
  a = nullptr;

  int * b = &y;  // error: invalid conversion from 'const int*' to 'int*'

  int const * c = &x;
  *c = 42; // error: assignment of read-only location '* c'
  c = nullptr;

  int const * d = &y;
  *d = 42; // error: assignment of read-only location '* d'
  d = nullptr;

  int * const e = &x;
  *e = 42;
  e = nullptr; // error: assignment of read-only variable 'e'

  int * const f = &y; // error: invalid conversion from 'const int*' to 'int*'

  int const * const g = &x;
  *g = 42; // error: assignment of read-only location '*(const int*)g'
  g = nullptr; // error: assignment of read-only variable 'g'

  int const * const h = &y;
  *h = 42; // error: assignment of read-only location '*(const int*)h'
  h = nullptr; // error: assignment of read-only variable 'h'
}
Last edited on
nuderobmonkey wrote:
But you cannot exchange the order of between the *'s

but we can...! you wrote it on your code! it can be written as:
 
const int * p1;


dutch wrote:
The rule is that const refers to the thing to it's left (the thing before it).
Obviously if it's the first word then there's nothing to it's left.
Only in that special case does it refer to the thing to it's right.

but if it's written like:
 
const char * const some_var[]

how it will be read..?
Last edited on
The some_var is an array of constant pointers to chars that are const.

order of between the *'s

Refers to:
1
2
int * const * a;
int * * const b;
Last edited on
Refers to:
1
2
int * const * a;
int * * const b;

don't understand... what does this means?
Surely you have seen pointers to pointers? The most common examples are dynamically allocated 2D array, and passing a pointer to function that must modify the caller's pointer (in C, C++ should use reference). Most of those examples lack const correctness. https://isocpp.org/wiki/faq/const-correctness

nuderobmonkey said that you can't move the const in those declarations without changing the meaning.
Your "but we can...!" was about a different thing.

Modern C++ offers solutions that let us write most of the code without explicit plain pointers, let alone pointers to pointers.
Note there is a potential 'gotcha' with using auto with a pointer - where what you get may not be what you expect! Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
	auto a1 {pf1()};		// char*
	const auto a2 {pf1()};		// char* const
	auto const a3 {pf1()};		// char* const

	auto* a4 {pf1()};		// char*
	const auto* a5 {pf1()};		// const char*
	auto const* a6 {pf1()};		// const char*
	auto* const a7 {pf1()};		// char* const
	auto const* const a8 {pf1()};	// const char* const
	const auto* const a9 {pf1()};	// const char* const
}


where pf1() returns a pointer to some memory.

Note the type of a2 and a3. These are const pointers to changeable memory. NOT a changeable pointer to const memory as might be expected. To get a pointer to const memory with auto, you have to specify auto*. See a5 and a6.
Last edited on
nuderobmonkey said that you can't move the const in those declarations without changing the meaning.
Your "but we can...!" was about a different thing.

no...! what he talking about is the position of the asterisk...!

but now maybe i got what he mean...

we can write either:
[specifier] [type]

or

[type] [specifier]

and followed with asterisk(s)

coz, it's unable to declare it like this:

1
2
3
int * const p1;
//or
const* int p2;


edit:
but after i read your code, there's declarations like that, but maybe that's what he wanna say...
Last edited on
Lets rephrase:
T ********sample;
(okay, that's really deep pointer to pointer to ...)

If we want that the concrete object fo type T is treated as constant, then we write:
T const ********sample;
but, we may use the alternate syntax:
const T ********sample;

However, if any of those asterisks has to be const-qualified, then the qualifier must be on the right side of that asterisk; there is no alternate syntax for that.

T *** * const ****sample;
Both the T and asterisks can be const-qualified.
T const *** * const ****sample;
There is an alternate syntax; it could make the code easier to read:

1
2
3
4
5
6
7
using T = std::string ;
using const_T = const T ;
using ptr_to_const_T = const_T* ;
using ptr_to_T = T* ;
using const_ptr_to_T = const ptr_to_T ;
using const_ptr_to_const_T = const  ptr_to_const_T ;
// etc. 


Visualising a type alias for the pointer may also help remove the seeplus confusion about potential 'gotcha' with using auto with a pointer. It would make it easy to see that there is no gotcha; that the rules of template argument deduction are consistently used for auto type deduction.
btw, another question is are double const are just for pointers?
e.g.:
1
2
const char* const some1;
const char* const some2 [] {"aljsd", "lakshdkah"};
yes just for pointers:

const before * means value pointed by pointer is constant, value can't be changed.
const after * mean the pointer is constant, pointer can't point to something else.


but you can type const multiple times for non pointer variables:

const const const const int x = 0;

it's not an error but compiler will ignore redundant const keywords, and use just one.

You won't see this but some people see to write things like this:

constexpr const int x = 0;

but compiler will ignore const in this case.

for pointers thing like this:

const int const * const int x = 0;

means second const before * is ignored, so the compiler takes it as:

const int * const x = 0;
Last edited on
there is probably a way to make an object that can use more consts if you really put some effort into doing so. I can't think of a practical use, but someone here will have an example.

the double const, think about what it means!
if only the pointer is constant, you can modify the data under it, but not point elsewhere.
if only the data is constant, you can move the pointer to another memory location, but not change the data.
if both are constant, its locked down both ways.
You can do:

1
2
constexpr const char*  mystr1 {"foobar1"};
constexpr const char* const mystrw {"foobar2"};

@seeplus

Indeed, good point!
I've just learned something new, which is that constexpr when applied to pointers doesn't imply const!

Instead it means that pointer is constant expression, but const is needed to declare whether data itself is const.
indeed, the above is part of the heavy overlap between pointers and references as well.
Pages: 123