robsn
robsn

Reputation: 744

Top-level or low-level constness or neither?

I'm working through the C++ Primer and if I understand it correctly:

// A plain int.
int i {0};

// Top-level const ints.
const int ci {42};
const int ci2 {0};

// A low-level pointer to const int.
const int * pci {&ci};

// Low-level, because the referenced object can't be changed.
*pci = 0; // error

// But not top-level, because it can be changed to point to another object.
pci = &ci2; // fine

// This is both top-level and low-level const
// because both the pointer and the object it
// points to are const:
const int * const cpci {&ci};
*cpci = 0;   // error
cpci = &ci2; // error

Now the question. Is there a naming convention for a constness which is neither top-level nor low-level? I.e. the pointer itself is not const but it points in a constant way to a non-const object? Or is this a special case of low-level constness? Example:

int i {0];
int j {42};

// The following pointer is not const itself.
// The object it's pointing to is not const but
// it can't be manipulated through the pointer.
const int * pci {&i};
*pci = 42; // error

// All these are fine:
++i;
pci = &j;
++j;

*pci = 42; // error as above

Edit after accepting Lightness Races in Orbit's answer:
My IDE calls them read-only pointers which makes sense to me although the referenced object can be changed with this ugly cast:

*const_cast<int*>(pci) = 21;

Upvotes: 4

Views: 1136

Answers (1)

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385144

No, not really.

The distinction you're making is between an object with const-qualified type, and an expression with const-qualified type.

Since (from a usage perspective) it very rarely matters which one is in play, there is no meaningful terminology to distinguish them in any given case.

This does admittedly make it a little cumbersome to explain why the following program is totally valid and well-defined (if really, really bad style), despite casting away the const:

void foo(const int& x)
{
   // the expression `x` has type `const int&` (before decay),
   // but after casting away the constness we can modify the
   // referent, because it happens to actually be non-`const`.
   const_cast<int&>(x) = 66;
}

int main()
{
   int x = 42;    // object is not const!
   foo(x);
}

For what it's worth, although "top-level const" is standard terminology, I'm not so sure about your "low-level const". The terms aren't even properly symmetrical! Pfft.

In foo() above we wouldn't actually write the const_cast because we would assume that all inputs are references to objects that really are const.

Upvotes: 3

Related Questions