Where to put default parameter value in C++?

222,351

Solution 1

Default parameter values must appear on the declaration, since that is the only thing that the caller sees.

EDIT: As others point out, you can have the argument on the definition, but I would advise writing all code as if that wasn't true.

Solution 2

You can do either, but never both. Usually you do it at function declaration and then all callers can use that default value. However you can do that at function definition instead and then only those who see the definition will be able to use the default value.

Solution 3

C++ places the default parameter logic in the calling side, this means that if the default value expression cannot be computed from the calling place, then the default value cannot be used.

Other compilation units normally just include the declaration so default value expressions placed in the definition can be used only in the defining compilation unit itself (and after the definition, i.e. after the compiler sees the default value expressions).

The most useful place is in the declaration (.h) so that all users will see it.

Some people like to add the default value expressions in the implementation too (as a comment):

void foo(int x = 42,
         int y = 21);

void foo(int x /* = 42 */,
         int y /* = 21 */)
{
   ...
}

However, this means duplication and will add the possibility of having the comment out of sync with the code (what's worse than uncommented code? code with misleading comments!).

Solution 4

Although this is an "old" thread, I still would like to add the following to it:

I've experienced the next case:

  • In the header file of a class, I had
int SetI2cSlaveAddress( UCHAR addr, bool force );
  • In the source file of that class, I had
int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force = false )
{
   ...
}

As one can see, I had put the default value of the parameter "force" in the class source file, not in the class header file.

Then I used that function in a derived class as follows (derived class inherited the base class in a public way):

SetI2cSlaveAddress( addr );

assuming it would take the "force" parameter as "false" 'for granted'.

However, the compiler (put in c++11 mode) complained and gave me the following compiler error:

/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp: In member function 'void CMax6956Io::Init(unsigned char, unsigned char, unsigned int)':
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: error: no matching function for call to 'CMax6956Io::SetI2cSlaveAddress(unsigned char&)'
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: note: candidate is:
In file included from /home/geertvc/mystuff/domoproject/lib/i2cdevs/../../include/i2cdevs/max6956io.h:35:0,
                 from /home/geertvc/mystuff/domoproject/lib/i2cdevs/max6956io.cpp:1:
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: int CI2cHal::SetI2cSlaveAddress(unsigned char, bool)
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note:   candidate expects 2 arguments, 1 provided
make[2]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/max6956io.cpp.o] Error 1
make[1]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/all] Error 2
make: *** [all] Error 2

But when I added the default parameter in the header file of the base class:

int SetI2cSlaveAddress( UCHAR addr, bool force = false );

and removed it from the source file of the base class:

int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force )

then the compiler was happy and all code worked as expected (I could give one or two parameters to the function SetI2cSlaveAddress())!

So, not only for the user of a class it's important to put the default value of a parameter in the header file, also compiling and functional wise it apparently seems to be a must!

Solution 5

If the functions are exposed - non-member, public or protected - then the caller should know about them, and the default values must be in the header.

If the functions are private and out-of-line, then it does make sense to put the defaults in the implementation file because that allows changes that don't trigger client recompilation (a sometimes serious issue for low-level libraries shared in enterprise scale development). That said, it is definitely potentially confusing, and there is documentation value in presenting the API in a more intuitive way in the header, so pick your compromise - though consistency's the main thing when there's no compelling reason either way.

Share:
222,351
Thomson
Author by

Thomson

Human debugger.

Updated on July 20, 2020

Comments

  • Thomson
    Thomson almost 4 years

    What's the place for the default parameter value? Just in function definition, or declaration, or both places?

  • Alex S
    Alex S over 13 years
    That may be technically correct, but I wouldn't consider it good advice.
  • Matthieu M.
    Matthieu M. over 13 years
    +1: even though you're technically allowed to choose, doing it in the declaration is the only self-documenting way.
  • Bo Persson
    Bo Persson over 13 years
    If you want to be REALLY horrible, you can actually do both, but for different parameters. :-)
  • sharptooth
    sharptooth over 13 years
    @Bo Persson: Great idea. Except we already have templates for being as horrible as one wants.
  • Janik Zikovsky
    Janik Zikovsky about 11 years
    @sharptooth: and macros :)
  • Mooing Duck
    Mooing Duck over 10 years
    I don't have a problem with these comments, since 99.99% of the time the parameter is simply value-initialized.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 10 years
    Relevant language: [C++11: 8.3.6/4]: [..] In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration [..]
  • Patrizio Bertoni
    Patrizio Bertoni about 9 years
    doing either for me produces the following g++ error: default argument given for parameter <x> of <fun(args)> [-fpermissive] ..... after previous specification in <fun(args)> [-fpermissive]
  • Lysol
    Lysol over 8 years
    I wonder if there are any cases where it would make sense for the defaults be implementation-specific. Which makes me wonder something else: Is it possible to mix it up? Like have one parameter have a default set in the implementation, and one in the declaration? Not sure why you'd want to do that. But still curious.
  • Alex S
    Alex S over 8 years
    @AidanMueller That's not possible. The default is passed by the caller based on the declaration it saw, not the value specified in the definition.
  • dwanderson
    dwanderson over 8 years
    Thought I needed it on both, so this was helpful, at least to say that you can't do both (though a little more on the why might be nice?)
  • Paulo Carvalho
    Paulo Carvalho almost 8 years
    Neither way works for me (MinGW). If I do it in the header I get "no matching function..." errors. If I do it in the cpp, I get linker errors... How I miss Java... I ended up writing an overloaded function.
  • Beyondo
    Beyondo over 5 years
    Cannot we assign the parameter to an object by default? like void test(obj, a, b = a);
  • CoffeeTableEspresso
    CoffeeTableEspresso over 3 years
    This is because the caller needs to know what parameters the function takes, so the declaration that the caller sees (normally in the header file) needs to have this information (including things like default values).
  • Gufino2
    Gufino2 almost 3 years
    That's because, even in virtual functions, the default value of the parameter is STATICALLY bound: that means that the default value of the parameter is chosen evaluating the static type of the variable. In this case, even if a points to a impl object, a is a ptr to iface, and when you call a->test() the compiler uses the default parameter from iface. That's why it's an HORRIBLE idea to change default param values in overrides. Scott Meyers talks about that in one of his books.