0

Consider the following code.

class SomeClass{};

class AnotherClass
{
public:
    SomeClass c1;
    SomeClass c2;
    SomeClass c3;
    const SomeClass* someClassArray[3] = { &c1, &c2, &c3 };
    
    const SomeClass** GetSomeClassArray () 
    {
      return someClassArray;
    }
};

int main ()
{
    AnotherClass anotherClass;
    const SomeClass** someClassArray = anotherClass.GetSomeClassArray ();
    
    SomeClass* someClass2 = new SomeClass;
    someClassArray[0] = someClass2; //This should not be allowed
}

I am declaring the array someClassArray as const. I am specifying the return type of GetSomeClassArray as const SomeClass** and yet, I am still allowed to modify the contents of the array later in the main function.

I also noticed that adding const specifier to the method as such: const SomeClass** GetSomeClassArray () const causes an error.

Why is this the case?

  • 1
    you have array of pointers to const objects and you are returning pointer to that array. You need `SomeClass* const* GetSomeClassArray()`. Or better use `std::array` it will be less confusing. – Marek R Jul 16 '21 at 18:03
  • 1
    You are looking for `const SomeClass* const*`. The second const is the one preventing overwriting pointers. – Ben Voigt Jul 16 '21 at 18:06
  • Cheat. The compiler knowns the type so tell it to do the job for you with `auto GetSomeClassArray ()` ;) – NathanOliver Jul 16 '21 at 18:09
  • Thanks that worked. Just want to clarify something. Is the second `const*` the type of `&c1, &c2 and &c3` in my specific example? –  Jul 16 '21 at 18:14

2 Answers2

1

Your interpretation of what is const is wrong.

The term const binds left (unless it is on the very left then in binds right).

// so This
const SomeClass* someClassArray[3] = { &c1, &c2, &c3 };

// Equivelent to this:
SomeClass const * someClassArray[3] = { &c1, &c2, &c3 };

So now we can read the type easier. Types are read right to left.

SomeClass const * [3]
                  ^^^ Array of
                ^     Pointer To
^^^^^^^^^^^^^^^       const SomeClass

So you have an array of pointer to "const SomeClass". So the "SomeClass" object are const but the pointers are not. If you want the array to be imutable then make the pointers also const.

SomeClass const * const [3]
                        ^^^ Array of
                ^^^^^^^     const Pointer To
^^^^^^^^^^^^^^^             const SomeClass

That being said. That's probably not the best way of declaring that.

Why not just declare an array of objects?

    const SomeClass someClassArray[3];

Also a return type of ** is loosing information on the size of the array, so that is not the greatest.

     // Updated to put const in correct place first.
     SomeClass const  *const  * GetSomeClassArray() {return someClassArray;}

     // The syntax for return an array by reference is 
     // super non intuitive. I could spend 30 minutes
     // looking up the exact syntax or we can let the
     // compiler do it for you.

     auto& GetSomeClassArray() {return someClassArray;}

or

     auto const& GetSomeClassArray() {return someClassArray;}

You want to keep the type information about the size of the array as this will help in sveral ways.


Since we are using C++ you can consider using std::array rather than a C array (or std::vector potentially).

  const std::array<SomeClass, 3> someClassArray;
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • And than change return type to `auto&`. – SergeyA Jul 16 '21 at 18:24
  • Thanks. That made a lot of sense. But rather than `SomeClass const * [3]` I think I prefer to think of it as `SomeClass* const [3]` and then read it as "const array of SomeClass pointer". Is there a downside to reading it this way? Like it might not work for more complicated data types? –  Jul 16 '21 at 18:30
  • 1
    @user16038533: Those are very different types. `SomeClass const *` The objects are const (i.e. you can only call const members and the values can not change). `SomeClass* const` The pointers are const (i.e you can change what is pointed at) but the value is not const and you can modify the `SomeClass` object (i.e. all methods can be called and you can change the values of the members). – Martin York Jul 16 '21 at 18:37
0

When you declare const SomeClass* p, it is *p that is const. So when you declare const SomeClass** p, it is **p that is const and not *p. If you want *p to be const, you have to put const SomeClass* const* p.

super
  • 278
  • 1
  • 11