16

The Google C++ Style Guide draws a clear distinction (strictly followed by cpplint.py) between input parameters(→ const ref, value) and input-output or output parameters (→ non const pointers) :

Parameters to C/C++ functions are either input to the function, output from the function, or both. Input parameters are usually values or const references, while output and input/output parameters will be non-const pointers.

And further :

In fact it is a very strong convention in Google code that input arguments are values or const references while output arguments are pointers.

But I can't figure out why input/output arguments (I leave output arguments aside) should not be passed by reference. On stackoverflow there are plenty of topics related to this question : e.g. here, the accepted answer clearly say that

it's mostly about style

but that if

you want to be able to pass null, you must use a pointer

So, what's the point to always demand a pointer if I want to avoid the pointer to be null ? Why only use references for input arguments ?

Community
  • 1
  • 1
suizokukan
  • 1,303
  • 4
  • 18
  • 33
  • 2
    If it's not a pointer, you know it's not going to be modified. This is useful information. If out parameters can be references, or in parameters can be pointers, you need to either constantly look at the documentation or remember everything. – molbdnilo Oct 18 '14 at 15:18
  • 7
    Just because it's from Google doesn't mean it must make sense. – Christian Hackl Oct 18 '14 at 15:29
  • 1
    FWIW, [Titus's CppCon talk](https://www.youtube.com/watch?v=NOCElcMcFik) – chris Oct 18 '14 at 15:31
  • 2
    The Google style guide contains a _lot_ of nonsense. For example, it disallows ALL C++ exceptions. – Lightness Races in Orbit Oct 18 '14 at 18:22
  • 1
    The Google C++ style guide appears to have changed. "Non-optional input parameters should usually be values or const references, while non-optional output and input/output parameters should usually be references (which cannot be null)" – Tom Tseng Dec 30 '22 at 04:17

4 Answers4

39

The reason for demanding that output parameters are passed as pointers is simple:

It makes it clear at the call site that the argument is potentially going to be mutated:

foo(x, y);     // x and y won't be mutated
bar(x, &y);    // y may be mutated

When a code base evolves and undergoes incremental changes that are reviewed by people who may not know the entire context all the time, it is important to be able to understand the context and impact of a change as quickly as possible. So with this style rule, it is immediately clear whether a change introduces a mutation.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I just want to mitigate a bit that maybe : let be a struct containing a pointer to an object. ```struct PtrStruct {int* a;};``` Now I'm passing it by value. ```PtrStruct s; /* some setup */ foo(s);```. My inner state can be mutated somehow, because I can access this inner pointer. What ensures non-mutability is more or less a combination of what the type is made of (pointers/refs or not, members private or not) and how it is passed (value/pointer/ref, const or not). That said, I still agree that the non non-const reference rule can make sense if set alongside rules to mitigate its use. – Felix Bertoni May 29 '21 at 17:24
9

The point they are making (which I disagree with) is that say I have some function

void foo(int a, Bar* b);

If the b argument is optional, or it is unnecessary sometimes, you can call the function like so

foo(5, nullptr);

If the function was declared as

void foo(int a, Bar& b);

Then there is no way to not pass in a Bar.

This point (emphasis mine) is completely opinion-based and up to the developer's discretion.

In fact it is a very strong convention in Google code that input arguments are values or const references while output arguments are pointers.

If I intend for b to be an output parameter, either of the following are perfectly valid and reasonable.

void foo(int a, Bar* b);  // The version Google suggests
void foo(int a, Bar& b);  // Reference version, also perfectly fine.
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • 4
    Titus seemed to lean more toward the callsite showing that whatever was passed in could be modified in his CppCon video. – chris Oct 18 '14 at 15:24
  • Thank you for your explanations : I understand it's "perfectly valid and reasonable" for the output arguments. But what about the input-output arguments ? I thought it was more dangerous to use pointers instead of references for them ! – suizokukan Oct 18 '14 at 15:33
  • @suizokukan, Nothing you said in your question suggests pointers for input parameters. – chris Oct 18 '14 at 15:34
  • Excuse me, it's not very clear to me and my English is so poor... By "input-output" argument I meant an argument modified by the function, not an input or an output argument. – suizokukan Oct 18 '14 at 15:48
4

You're first question: "So, what's the point to always demand a pointer if I want to avoid the pointer to be null?"

Using a pointer announces to the caller that their variable may be modified. If I am calling foo(bar), is bar going to be modified? If I am calling foo(&bar) it's clear that the value of bar may be modified.
There are many examples of functions which take in a null indicating an optional output parameter (off the top of my head time is a good example.)

Your second question: "Why only use references for input arguments?"

Working with a reference parameter is easier than working with a pointer argument.

int foo(const int* input){
    int return = *input;

    while(*input < 100){
        return *= *input;
        (*input)++;
    }
}

This code rewritten with a reference looks like:

int foo(const int& input){
    int return = input;

    while(input < 100){
        return *= input;
        input++;
    }
}

You can see that using a const int& input simplifies the code.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
1

They likely use it for consistency because they use output parameters both as references to existing memory (they're modifying previously initialized variables) and as actual outputs (the output arguments are assumed to be assigned by the function itself). For consistency, they use it as a way to more clearly indicate inputs vs. outputs.

If you never need a function/method to assign the memory of the output parameter, like returning a pointer from a lookup or allocating memory itself and returning it through a pointer, use references. If you need to do that but don't care about using pointers to act as an indication of whether a parameter is input or output, use references for output parameters when appropriate. There's no absolute requirement to use pointers in all cases unless the requirements of that function/method itself requires it.