C++ Const Usage Explanation

const int* const Method3(const int* const&) const;

Can someone explain the usage of each of the const?

60440 次浏览

Read this: https://isocpp.org/wiki/faq/const-correctness

The final const means that the function Method3 does not modify the non mutable members of its class.

const int* const means a constant pointer to a constant int: i.e. a pointer that cannot be changed, to an int that cannot be changed: the only difference between this and const int& is that it can be null

const int* const& means a reference to a constant pointer to a constant int. Usually pointers are not passed by reference; const int* & makes more sense because it would mean that the pointer could be changed during the method call, which would be the only reason I can see to pass a pointer by reference, const int* const& is to all intents and purposes the same as const int* const except that it is probably less efficient as pointers are plain old data (POD) types and these should, in general be passed by value.

First of all const T is equivalent to T const.

const int* const is therefore equivalent to int const * const.

When reading expressions with lots of const tokens and pointers in them, always try to read them from right to left (after applying the transformation above). So in this case the return value is a const pointer to a const int. Making the pointer itself const makes no sense here since the return value isn't a lvalue that could be modified. Making the pointee const, however, guarantees that the caller may not modify the int (or array of ints) returned by Method3.

const int*const& becomes int const*const&, so it is a reference to a const pointer to a const int. Passing a const pointer by references male no sense either - you can't modify the referenced value since the pointer is const and references and pointers occupy equal storage so there aren't any space savings either.

The last const indicates that the method does not modify the this object. The this pointer within the method body will have the (theoretical) declaration T const * const this. This means that a const T* object will be able to call T::Method3().

I like to use the "clock" or "spiral" method where starting from the identifier name (in this case Method3) you read back-and-forth from left-to-right-back-to-left, etc. in order to decode naming conventions. So const int* const Method3(const int* const&) const is a class method that doesn't change any class members (of some un-named class) and takes a constant reference to a pointer that points to a constant int and returns a constant pointer to a constant int.

Hope this helps,

Jason

Reading from right to left makes understanding modifiers easier.

A const method that takes a reference to a const pointer to a const int called Method3 which returns a const pointer to a const int.

  1. A const method can't modify members (unless they are expicitly mutable)
  2. A const pointer can't be changed to point to something else
  3. A const int (or any other type) can't be modified
const /* don't modify the int or array of ints' value(s) */
int* const /* as a retval, ignored. useless declaration */
Method3(const /* don't modify the int or array of ints' value(s) */
int* const /* don't modify the pointer's value, the address to which `pointer` points to. e.g. you cannot say `++pointer` */
&) const; /* this method does not modify the instance/object which implements the method */
  • const at the end of the method is the qualifier signifying the state of the object is n't going to be changed.

  • const int*const& signifies receiving by reference a const pointer to a const location. It can neither change to point to a different location nor change the value it is pointing at.

  • const int*const is the return value which is also a constant pointer to a constant location.

const #1: The pointer returned by Method3 refers to a const int.

const #2: The pointer value returned by the function, itself, is const. This is a useless const (though gramatically valid), because the return value from a function cannot be an l-value.

const #3: The pointer type passed by reference to the function points to a const int.

const #4: The pointer value passed by reference to the function is, itself, a const pointer. Declaring a value that is passed to a function as const would normally be pointless, but this value is passed by reference, so it can be meaningful.

const #5: The function (presumably a member function) is const, meaning that it is not allowed to (a) assign new values to any members of the object of which it is part or (b) call a non-const member function on the object or any of its members.

It's easier to understand if you rewrite that as the completely equivalent

// v───v───v───v───v───v───v───v───v───v───v───v─┬┐
//                                               ││
//  v──#1    v─#2             v──#3    v─#4      #5
int const * const Method3(int const * const&) const;

then read it from right to left.

#5 says that the entire function declaration to the left is const, which implies that this is necessarily a member function rather than a free function.

#4 says that the pointer to the left is const (may not be changed to point to a different address).

#3 says that the int to the left is const (may not be changed to have a different value).

#2 says that the pointer to the left is const.

#1 says that the int to the left is const.

Putting it all together, you can read this as a const member function named Method3 that takes a reference to a const pointer to an int const (or a const int, if you prefer) and returns a const pointer to an int const (const int).

(N.b. #2 is entirely superfluous.)

An easy way to remember the rules of const is to think about it this way: const applies to the thing on its left, unless there's nothing on its left.

So in the case of const int * const, the first const has nothing on its left, so it applies to int and the second one does have something on its left, so it applies to the pointer.

This rule also tells you what would happen in the case where you have const int const *. Since both const's apply to int this expression is redundant and therefore invalid.

A few examples might be nice to demonstrate this concept, the more the better imho.

class TestClass
{
private:
int iValue;
int* oValuePtr;
int& oValueRef;


public:
int TestClass::ByValMethod1(int Value)
{
// Value can be modified
Value++;


// iValue can be modified
iValue = Value;
iValue += 1;


// Return value can be modified
return ++iValue;
}


int TestClass::ByValMethod2(const int Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;


// iValue can be modified
iValue = Value;
iValue += 1;


// Return value can be modified
return ++iValue;
}


const int TestClass::ByValMethod3(int Value)
{
// Value can be modified
Value++;


// iValue can be modified
iValue = Value;
iValue += 1;


// Return value can be modified
return ++iValue;
}


const int TestClass::ByValMethod4(const int Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;


// iValue can be modified
iValue = Value;
iValue += 1;


// Return value can be modified
return ++iValue;
}


const int TestClass::ByValMethod5(const int Value) const
{
// Value *cannot* be modified
// Variable is const variable
Value++;


// iValue *cannot* be modified
// Access through a const object
iValue = Value;
iValue += 1;


// Return value *cannot* be modified
// Access through a const object
return ++iValue;
}


int& TestClass::ByRefMethod1(int& Value)
{
// Value can be modified
Value++;


// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;


// Return value can be modified
return ++oValueRef;
}


int& TestClass::ByRefMethod2(const int& Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;


// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;


// Return value can be modified
return ++oValueRef;
}


const int& TestClass::ByRefMethod3(int& Value)
{
// Value can be modified
Value++;


// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;


// Return value can be modified
return ++oValueRef;
}


const int& TestClass::ByRefMethod4(const int& Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;


// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;


// Return value can be modified
return ++oValueRef;
}


const int& TestClass::ByRefMethod5(const int& Value) const
{
// Value *cannot* be modified
// Variable is const variable
Value++;


// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;


// Return value can be modified
return ++oValueRef;
}


int* TestClass::PointerMethod1(int* Value)
{
// Value can be modified
Value++;


// oValuePtr can be assigned
oValuePtr = Value;


// oValuePtr can be modified
oValuePtr += 1;


// Return value can be modified
return ++oValuePtr;
}


int* TestClass::PointerMethod2(const int* Value)
{
// Value can be modified
Value++;


// oValuePtr cannot be assigned
// const int* to int*
oValuePtr = Value;


// oValuePtr can be modified
oValuePtr += 1;


// Return value can be modified
return ++oValuePtr;
}


const int* TestClass::PointerMethod3(int* Value)
{
// Value can be modified
Value++;


// oValuePtr can be assigned
oValuePtr = Value;


// iValue can be modified
oValuePtr += 1;


// Return value can be modified
return ++oValuePtr;
}


const int* TestClass::PointerMethod4(const int* Value)
{
// Value cannot be modified
Value++;


// oValuePtr *cannot* be assigned
// const int* to int*
oValuePtr = Value;


// oValuePtr can be modified
oValuePtr += 1;


// Return value can be modified
return ++oValuePtr;
}


const int* TestClass::PointerMethod5(const int* Value) const
{
// Value can be modified
++Value;


// oValuePtr *cannot* be assigned
// const int* to int* const
// Access through a const object
oValuePtr = Value;


// oValuePtr *cannot* be modified
// Access through a const object
oValuePtr += 1;


// Return value *cannot* be modified
return ++oValuePtr;
}
};

I hope this helps!

An easy way to remember the const in C++ is when you see some code in form like:

XXX const;
const YYY;

XXX, YYY will be a constant component,
XXX const form:

function ( def var ) const;    ------#1
* const;                       ------#2

const YYY form:

const int;                     ------#3
const double;

People usually use these types. When you see "const&" somewhere, don't feel confused, const is describing something before itself. so the answer of this problem is self-evident now.

const int* const Method3(const int* const&) const;
|          |             |          |       |
#3         #2            #3         #2      #1

I only want to mention that const int* const& is indeed a constant reference to const int*. For example:

int i = 0;
int j = 1;
int* p = &i;
int* q = &j;
const int* const& cpref = p;
cpref = q; //Error: assignment of read-only reference 'cpref'

It's also the case for int* const&, Which means:"A constant reference to int*".
But const int*& is a non-constant reference to const int*.
Hope this helps.