5

So, I know that you can pass a function as an argument like so:

int a(int x) {
    return x + 1;
}

int b(int (*f)(int), int x) {
    return f(x); // returns x + 1
}

I also know you can have a function with default arguments, like so:

int a(int x = 1) {
    return x;
}

a(2); // 2
a();  // 1

However, how do I pass a function with default arguments to a function and preserve this behavior?

I've tried the following:

int b(int (*f)(int), int x) {
    f(x); // works as expected
    f();  // doesn't work because there aren't enough arguments to f
}

and

int b(int (*f)()) {
    f();  // doesn't work because it cannot convert int (*)(int) to int (*)()
}
8
  • 1
    This will be tricky, as default arguments are a compile-time operation. The compiler, seeing the definition int a(int x = 1), will turn your a(); into a(1); and compile that. Commented Dec 8, 2021 at 21:43
  • 1
    default arguments are applied at the call site at compile time based on what the compiler can see. If the compiler can't be 100% certain what function is at the other end of that function pointer, there's pretty much no way to apply a default argument. Even if there is only ever one function being used, that function could have different declarations at different points in the code with different default arguments or values on those arguments. Because of that I wouldn't be surprised if the compiler doesn't even try to sort it out. Commented Dec 8, 2021 at 21:51
  • 2
    @DrewDormann Oh, I see, that makes sense. Thanks, I will probably circumvent this by creating a callable object instead. Commented Dec 8, 2021 at 21:52
  • 2
    @hyper-neutrino Callable objects should be the first option in C++ over function pointers, IMO. Function pointers do not carry state, and you can't do anything with them other than what basically C can do. With a callable object, you basically can do anything, including moving the entire parameter determination away from the call invocation site, all due to operator(). This is something you can't do with "dumb" function pointers. Commented Dec 8, 2021 at 21:57
  • 1
    The default parameter is not part of the signature. So you cannot discriminate between, for instance, void f(int=0) and void g(int=1). You will have to wrap the function together with the default parameter. Commented Dec 8, 2021 at 22:28

2 Answers 2

3

There is no way to forward default parameter values with function pointers.

But you can make it work if you can turn b into a templated function:

By using a parameter pack for operator() and explicitly calling a we give the compiler the opportunity to apply the default argument values if needed:

int a(int x = 12) {
    return x + 1;
}

template<class T>
int b(T f, int x) {
    return f() + f(x);
}

struct AFnWrapper {
    template<class... Args>
    auto operator()(Args&&... args) {
        return a(std::forward<Args>(args)...);
    }
};

int main() {
   std::cout << b(AFnWrapper{}, 1) << std::endl; // 15
}

A shorter version of this would be to just use a lambda:

std::cout << b([](auto&&... args) { return a(std::forward<decltype(args)>(args)...); }, 1) << std::endl;

If you don't need perfect forwarding you can make it even shorter:

std::cout << b([](auto... args) { return a(args...); }, 1) << std::endl;

godbolt example

Sign up to request clarification or add additional context in comments.

Comments

3

It's not possible to do this with function pointers, as function pointers don't have the ability to store information about default arguments. However, you can pass around a function object that happens to have a member function with default arguments.

Converting a to such an object is straightforward; just make it a lambda

auto a = [](int x = 1) {
    // ...
};

and then b can take a function object, where the type is deduced

template<typename F>
int b(F f, int x) {
    f(x);  // ok
    f();   // also ok, default value is 1
}

Here's a demo.

Comments

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.