2

Is there a simple way to pass a non-const C++ structure (via pointer) into a function, and make sure that the function cannot alter members of the struct?

Ex: A structure is loaded from a config file, which requires the struct to be non-const. But then a pointer to the struct is passed into a function that is not permitted to alter anything.

I've seen this done by creating a second verbatim 'const' copy of the struct, and tediously copying every member. That seems odd. I'm a C# programmer though, so perhaps that is the way it is done in C++...?

StringTheory
  • 117
  • 3
  • 8
  • 4
    Why not just using a const reference as parmeter: `const type& argname`? Another possibility would be a pointer to a constant type: `const type* argname`, but the first method is the common way. – Andre Kampling Aug 23 '17 at 09:24
  • 2
    "A structure is loaded from a config file, which requires the struct to be non-const." - not true. Implement a constructor for the structure that loads the config file, then you can do something like `const Config config("config.txt")` – virgesmith Aug 23 '17 at 09:32

2 Answers2

7

In C++ you can pass big objects as reference or pointer both is called "Call by reference" in contrast to "Call by value" where a copy is made. The pointer syntax is inherited by the C language and differs from C++ references.

To make the compiler complain about changes to your object you have to use the const keyword. Sure you can cast the constness away to change the values anyway, but casting constness away isn't recommended at all.

Let's assume for the following examples that you have a class BigObject with a public member a which is an integer int.

1. Call by reference (&: reference type):

void function(const BigObject& input)
{
   int a = input.a; /* read from BigObject     */
   input.a = 42;    /* the compiler will fail! */
}

/* call that function */
BigObject input();
function(input); /* no address operator needed */

The const keyword will make the reference & (not the address operator here) refer to data that can't be changed by this reference. If you try that the compiler will fail compiling.

2. Call by reference (*: pointer type):

void function(const BigObject* input)
{
   int a = input->a;  /* read from BigObject     */
   int b = (*input).a /* alternative to ->       */
   input->a = 42;     /* the compiler will fail! */
}

/* call that function */
BigObject input();
function(&input); /* pass address */

The argument is a pointer type which points to data which can't be changed by this pointer. You can also make the pointer constant by using: const BigObject* const input, more explanation about this follows below.


Here are the differences between references and pointers:

  • A pointer needs to be dereferenced, this is done by the dereference (star) operator * or with -> for class/struct members, see code above.
  • Further you have to pass the address of the object using the address operator & to get a pointer.
  • Another difference is, that references can't be NULL they have to be valid, but a pointer can point to NULL.

Placement of const keyword:

The placement of the const keyword decides about what should be constant, the pointer or the object to which the pointer points.
In general there are the following which you can easier remember if you just think of a vertical line going through the * (star). If const is on the left of the * it will apply to the type (object), if it's on the right it will apply to the pointer:

                |
BigObject       *        input /* pointer to BigObject             */
const BigObject *        input /* pointer to constant BigObject    */
BigObject const *        input /* same as before, I don't use this */
BigObject       * const  input /* constant pointer to BigObject    */
                |

which you can also combine to make both constant:

                |
const BigObject * const  input /* constant pointer to constant BigObject */
                |

The placement of the const keyword doesn't matter on references. A reference is always constant (this is implicit and not named explicitly), means it can't reference to another object once it was set. The object is mutable however. This means the const keyword on the right side of & would be redundant and not change anything, just don't use that. You should just differ between:

BigObject&        input /* reference to BigObject           */
const BigObject&  input /* reference to constant BigObject  */
BigObject const & input /* same as before, I don't use this */
Andre Kampling
  • 5,476
  • 2
  • 20
  • 47
  • 1
    Very lucid replies--and code examples from Andre! (I have to wonder now why the original code that I was referring to used such verbose and error-prone member-by-member copies. I thought that I had missed something about C++ syntax). I have seen much use of "&" refs, and very little use of "->" in recent code, so Andre's comment about null pointers also sheds some light. I'll stick with "&." Easy to miss nuance, given the complexity of the language. – StringTheory Aug 23 '17 at 19:21
  • Just be aware of where you place the `const`, as placement matters. `const BigObject * input` (same as `BigObject const * input`) means "*a non-const pointer to a const object*" - the object cannot be modified, but the pointer can be. That is different than `BigObject * const input`, which means "*a const pointer to a non-const object*" - the object can be modified, but the pointer cannot. Then there is `BigObject const * const input` (and `const BigObject * const input`), which means "*a const pointer to a const object" - neither can be modified. – Remy Lebeau Aug 23 '17 at 21:01
  • For references, `const BigObject & input` (same as `BigObject const & input`) is different than `BigObject & const input` (effectively the same as `BigObject & input`). The first is a reference to a const object, so the object cannot be modified, whereas the second is a reference to a non-const reference, so the object can be modified – Remy Lebeau Aug 23 '17 at 21:05
  • @AndreKampling: `const` applies to the item on its left, unless there is nothing there then it applies to the item on the right instead. So `const BigObject` and `BigObject const` are the same thing. `* const` means "const pointer", not "pointer to a const" – Remy Lebeau Aug 23 '17 at 21:09
  • Hi @StringTheory if this or any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/q/5234/179419) by clicking the check-mark. This indicates to the wider community that you've found a solution and gives some reputation to both the answerer and yourself. There is no obligation to do this. – Andre Kampling Mar 26 '18 at 18:39
3

The solution is simple: Pass a pointer to const to the function instead (or, perhaps a reference would be more appropriate). The function will not be able to modify the object through the pointer to const.

eerorika
  • 232,697
  • 12
  • 197
  • 326