6

When searching for how to pass functions as parameters in C++, I only find examples that use function pointers. However the following compiles and outputs "g20" as expected in Visual Studio. Is it better to declare f like this:

f(void (*fun)());

instead of

f(void fun());

my example:

#include <iostream>

using namespace std;

int f(void fun());
void g();

int main() {
    cout << f(g);
}

void g() {
    cout << "g";
}

int f(void fun()) {
    fun();
    return 20;
}
manlio
  • 18,345
  • 14
  • 76
  • 126
a3920
  • 81
  • 1

5 Answers5

4

You might prefer std::function<> instead of function pointers. It can not only store function pointers, but also lambdas, bind expressions, function objects (objects with operator()), etc. Especially the lambdas will make your API a lot better usable.

int f(std::function<void()>& fun) {
    fun();
    return 20;
}
cdonat
  • 2,748
  • 16
  • 24
  • Passing `std::function` by reference is usually not necessary in my experience. Since that type behaves like a function pointer, you might as well pass by value. – amon Nov 11 '15 at 10:20
  • 1
    @amon AFAIK the `std::function<>` copy constructor will copy the target. That is fine for function pointers, but might be expensive for function objects, or lambdas with elements captured by value. – cdonat Nov 11 '15 at 10:24
  • Using a non-const reference is incorrect. Either use pass by value or pass by const reference. – smerlin Nov 11 '15 at 11:17
  • 1
    @smerlin with a const reference, the target if the `function<>` would have to be const as well. A function object, that has a non const `operator()` could not be invoked that way. – cdonat Nov 11 '15 at 11:19
  • @cdonat: the operator() of `std::function` is `const`. That is the only relevant part. So using a const reference is the way to go. – smerlin Nov 11 '15 at 11:21
  • @smerlin when the `std::function<>` object is `const`, the target it holds is `const` as well. Whenever that target is a function object with a non const `operator()` how can the `operator()` in `std::function<>` call it? – cdonat Nov 11 '15 at 11:25
  • @cdonat: The contained functor is probably stored as a pointer in the `std::function` object, and thus can be accessed and mutated in const member functions of `std::function`. here is an example showing that it works: http://ideone.com/OPykci – smerlin Nov 11 '15 at 11:41
  • @smerlin I prefer not to walk around with assumptions about how one or another implementation of the standard library handles the implementation specific details. Do you have a link to some standard document, or at least comprehensive documentation, that states, that one can use `const function<>` that way? Not just your experiment with whatever compiler and library version you might have chosen. – cdonat Nov 11 '15 at 11:49
  • @cdonat: no i cannot be bothered to do that right now. It should be obvious anyway. The `operator()` of `std::function` is `const`-qualified. How different compilers implement the details is not relevant at all for this question. – smerlin Nov 11 '15 at 12:06
0

Another way which is simple and does not have the cost of a std:::function is to use templates.

template <typename Function>
int f(Function function) {
  function();
  return 20;
}

That way the type will be deduced to be any kind of callable object. It should also enable the compiler to inline the call if it can (which is not possible with std::function).

Adrien Hamelin
  • 395
  • 3
  • 9
0

Both forms are equivalent. I prefer the form which explicitly shows that the parameter is a pointer. The knowledge, that the parameter is a pointer is important, since you can pass the values NULL, nullptr or 0 as argument. Your program would compile, but crash if someone would do the function call f(0). You always want to check if a function pointer is not a null pointer before calling the pointee, unless you are certain that it is not possible that your function is called with a NULL, nullptr or 0 argument.

If you use lambdas in your project, you should use `templates. Otherwise you can continue to use raw function pointers, but make sure that you check your function pointer (if necessary)

template <typename Function>
int f(const Function& functionp) {
  if(functionp)
     functionp();
  return 20;
}

Lambdas and std::function<> objects also have a bool operator, so the line if(functionp) will also work for those. It will evaluate to false for std::function<> objects which contain a nullptr and otherwise it will evaluate to true for std::function<> objects and lambdas.

smerlin
  • 6,446
  • 3
  • 35
  • 58
0

Ampersand & makes it a reference, std::decay_t makes reference a pointer.

template <class R, class Args...>
using func_ref_t = R(&)(Args...)

template <class R, class Args...>
using func_ptr_t = R(&)(Args...)
bobah
  • 18,364
  • 2
  • 37
  • 70
0

According to the C (and C++) standard, when a parameter has a function type, the compiler automatically adjusts it to the corresponding function pointer type. Therefore, the two are identical as far as the compiler is concerned.

C99 standard section 6.7.5.3 paragraph 8:

A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to function returning type’’, as in 6.3.2.1.

C++03 standard section 8.3.5 paragraph 3:

[...] After determining the type of each parameter, any parameter of type [...] “function returning T” is adjusted to be [...] “pointer to function returning T,” respectively. [...]

newacct
  • 119,665
  • 29
  • 163
  • 224