3

Is there a nice way to have a non static value as default argument in a function? I've seen some older responses to the same question which always end up in explicitly writing out the overload. Is this still necessary in C++17?

What I'd like to do is do something akin to

class C {
  const int N; //Initialized in constructor

  void foo(int x = this->N){
    //do something
  }
}

instead of having to write

class C {
  const int N; //Initialized in constructor

  void foo(){
    foo(N);
  }

  void foo(int x){
    //do something
  }
}

which makes the purpose of the overload less obvious.

7
  • 1
    Good question, shows research before asked. The reasons for why this is not possible are here. However, I don't know about something specific to c++17.. Commented Jul 20, 2019 at 14:19
  • "Not really" is the short answer here. In the past, in similar situations, I ended up either sucking up and declaring an overload (relying on the compiler to optimize out the extra function call), or change the parameters that get passed in, somehow, in order to eliminate the explicit default. Commented Jul 20, 2019 at 14:20
  • @gsamaras Thanks! I suppose this means the problem can't be solved without breaking backwards compatibility. Commented Jul 20, 2019 at 14:23
  • @DeinFreund I don't know, maybe std::optional could come to the rescue, but I would follow Sam's advice and refactor. Commented Jul 20, 2019 at 14:26
  • What if N is static? Commented Jul 20, 2019 at 14:44

1 Answer 1

2

One relatively elegant way (in my opinion) would be to use std::optional to accept the argument, and if no argument was provided, use the default from the object:

class C {
  const int N_; // Initialized in constructor
    public:
    C(int x) :N_(x) {}

  void foo(std::optional<int> x = std::nullopt) {
        std::cout << x.value_or(N_) << std::endl;
  }
};

int main() {
  C c(7);
  c.foo();
  c.foo(0);
}

You can find the full explanation of what works/doesn't work in section 11.3.6 of the standard. Subsection 9 describes member access (excerpt):

A non-static member shall not appear in a default argument unless it appears as the id-expressionof a class member access expression (8.5.1.5) or unless it is used to form a pointer to member (8.5.2.1).[Example:The declaration of X::mem1()in the following example is ill-formed because no object is supplied for the non-static memberX::a used as an initializer.

int b;
class X {
   int a;
   int mem1(int i = a);// error: non-static memberaused as default argument
   int mem2(int i = b);// OK; useX::b
   static int b;
};
Sign up to request clarification or add additional context in comments.

3 Comments

Doesn't this replace the compile time differentiation by a run time conditional, which would impact the performance? I also wouldn't want to have to add a list of nulls to a function call.
It possibly does, but that can often be optimized out by the compiler (godbolt.org/z/7HcJnQ). I am not sure what you mean by a list of nulls tbh, but obviously there are some trade-offs, and if you want this to be discerned at compile time, overloads are likely your best shot. This can also be done with template magic: github.com/rmpowell77/LIAW_2017_param, but it requires a non-trivial amount of meta-programming and is currently still not easy to do.
I would just change void foo(std::optional<int> x) to void foo(std::optional<int> x = std::nullopt) as that would allow to do the call without the optional arguments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.