How to handle default values ​​in (deeply) nested function calls?

Suppose there is a function with a default value:

int foo(int x=42); 

If it is called by others, for example:

 int bar(int x=42) { return foo(x); } int moo(int x=42) { return bar(x); } 

This, of course, is just a contrived example. However, sometimes I have a very similar situation. The parameter is just passed from the highest level ( moo ) to the lowest and only there it is actually used. The bad thing about this is that when I change foo to have a default value other than 42 , I would have to look for all the callers and change the default value accordingly.

Is there any template / idiom to avoid this situation?

The only simple solution that comes to my mind:

 int bar() { return foo(); } int bar(int x) { return foo(x); } 

However, since I am a bit lazy in real code as well, this will lead to pretty duplicate code, I would like to avoid this.

+6
source share
3 answers

Practical common solutions include:

  • Use the Optional_ class for the argument, for example boost::optional or the DIY equivalent.

  • What is the default value (and use the name in shell function definitions).

  • Overload each wrapper function as shown in the question.

  • Just repeat the default value in the definitions of shell functions, but this violates the DRY principle, do not repeat yourself.


Toby commented on the else-thread comment as an example of an asdf shell defined as

 int asdf(int x=42,int y=42){ return foo(x)+foo(y);} 

Using the Optional_ class:

 auto foo( Optional_<int> x) -> int { return (x.is_empty()? 42 : x.value()); } auto asdf( Optional_<int> x = {}, Optional_<int> y = {} ) -> int { return foo( x ) + foo( y ); } 

Using a named default:

 int const foo_default = 42; auto foo( int x = foo_default ) -> int { return x; } auto asdf( int x = foo_default, int y = foo_default ) -> int { return foo( x ) + foo( y ); } 

Using overloads:

 auto foo( int x = 42 ) -> int { return x; } auto asdf() -> int { return foo() + foo(); } auto asdf( int x ) -> int { return foo( x ) + foo(); } auto asdf( int x, int y ) -> int { return foo( x ) + foo( y ); } 

It is worth noting that asdf cannot be easily defined as a function template that forwards its arguments. In addition, such a template cannot be easily defined in a separate translation unit, and its address is not possible. For these reasons, I did not include this possible solution in the bullet list: it is very limited, not a general solution.

+2
source

I would suggest choosing one of these two options below (as you can see in the other answers - there are more possible solutions).

  • Overloading your functions
  • Define a constant

So option 1 will look like this:

 int foo(int x=42); int bar(int x) { return foo(x); } int moo(int x) { return bar(x); } int bar() { return foo(); } int moo() { return bar(); } 

And option 2 will be a little shorter:

 constexpr int FOO_DEFAULT = 42; int foo(int x=FOO_DEFAULT); int bar(int x=FOO_DEFAULT) { return foo(x); } int moo(int x=FOO_DEFAULT) { return bar(x); } 

I would use option-1 for cases with a small number of default values ​​(as one default value), option-2 for cases when you have several default values ​​- for example foo(int a, int b = 3, std::string c = "wow", float pi = 3.14)

+6
source

You can avoid duplication with:

 template<typename... T> auto bar(T&&... t) { return foo(std::forward<T>(t)...); } 

But this is not an improvement IMHO. Just stop being lazy and define overloads that call foo() when no argument is provided.

+3
source

All Articles