Function Pointer VS Function Reference

Function pointer vs Function reference

Functions and function references (i.e. id-expressions of those types) decay into function pointers almost immediately, so the expressions func and f_ref actually become function pointers in your case. You can also call (***func)(5) and (******f_ref)(6) if you like.

It may be preferable to use function references in cases where you want the &-operator to work as though it had been applied to the function itself, e.g. &func is the same as &f_ref, but &f_ptr is something else.

When to use function reference instead of function pointer in C++?

when you define a reference:

void (&fr)() = foo;
fr();

it gives you the ability to use fr almost everywhere where it would be possible to use foo, which is the reason why:

fr();
(*fr)();

works exactly the same way as using the foo directly:

foo();
(*foo)();

One more Difference is dereferencing a function reference doesn't result in an error where function pointer does not require dereferencing.

C++ What's the difference between function_pointer and &function_pointer?

Function designators are implicitly converted to pointers to the function types.

From the C++ Standard (4.3 Function-to-pointer conversion)

1 An lvalue of function type T can be converted to a prvalue of type
“pointer to T.” The result is a pointer to the function

So these declarations

    int (*f)() = &No; //method 1
int (*f2)() = No; //method 2

are equivalent. In the first declaration the function designator is converted to pointer explicitly while in the second declaration the function designator is converted to pointer implicitly.

In the call of the function in this statement

std::cout << (*f)() << std::endl;

using the expression *f is redundant because again the function designator *f is converted to pointer. You could just write

std::cout << f() << std::endl;

If you want you can even write

std::cout << (*******f)() << std::endl;

The result will be the same.

Here is a demonstrative program.

#include <iostream>

int No() { return 5; }

int main()
{
int (*f)() = &No; //method 1
int (*f2)() = No; //method 2

std::cout << f() << std::endl; //prints out 5
std::cout << ( *******f2 )() << std::endl; //prints out 5

return 0;
}

That is dereferencing the pointer *f2 yields lvalue of the function type that in turn again is converted to function pointer.

Pointers vs. References in C++ function arguments

The first approach is called "passing by pointer"; the second approach is called "passing by reference". In the first case, dereference is explicit; in the second case, dereference is implicit.

The biggest difference between the two approaches is that when you pass by pointer, you can pass "nothing" (i.e. a null pointer). When you pass by reference, it is not possible to legally pass a reference to nothing: it should be a reference to some variable, an array element, a field of a class or a structure, etc.

When you need to return a value and modify a variable, passing by reference is more appropriate, because the variable that you need to modify always exists. Passing by pointer becomes more appropriate in situations when you traverse a dynamic data structure connected by pointers, when parts of that data structure may or may not exist.

Why can I assign to a function pointer both a reference to a function a the function itself?

This is pretty much an exact duplicate of this question

The answer is that functions can be implicitly converted to function pointers (similar to array to pointer decay) per the spec.

Function Pointers by Reference

Your premise is false. You don't need an & in front of a function to pass it to delIf. The name of a function decays into a pointer to the function almost everywhere it is used in an expression. (Including when you call the function!) In fact, the only place it doesn't is when it is used as an argument to & - so

func
&func

have exactly the same type and value.

Having said that, yes you can pass a reference. First rule of pointers to functions - write a typedef

typedef bool pred_t(T);
void delIf( pred_t& pred );

But! I strongly encourage you to write delIf as a function template, and allow anything which can be called with a T, and has a function result which can be implicitly converted to bool.

template <typename Pred>
void delIf(Pred pred) {
...
}

That will allow use with capturing lambdas, and functors in general.

Also, what is this CPP file of which you speak? Templates have to be implemented in the header file. See the answers to this question.

(Note: "Pred" is short for "predicate" which is what the standard calls this sort of function.)

Should I use std::function or a function pointer in C++?

In short, use std::function unless you have a reason not to.

Function pointers have the disadvantage of not being able to capture some context. You won't be able to for example pass a lambda function as a callback which captures some context variables (but it will work if it doesn't capture any). Calling a member variable of an object (i.e. non-static) is thus also not possible, since the object (this-pointer) needs to be captured.(1)

std::function (since C++11) is primarily to store a function (passing it around doesn't require it to be stored). Hence if you want to store the callback for example in a member variable, it's probably your best choice. But also if you don't store it, it's a good "first choice" although it has the disadvantage of introducing some (very small) overhead when being called (so in a very performance-critical situation it might be a problem but in most it should not). It is very "universal": if you care a lot about consistent and readable code as well as don't want to think about every choice you make (i.e. want to keep it simple), use std::function for every function you pass around.

Think about a third option: If you're about to implement a small function which then reports something via the provided callback function, consider a template parameter, which can then be any callable object, i.e. a function pointer, a functor, a lambda, a std::function, ... Drawback here is that your (outer) function becomes a template and hence needs to be implemented in the header. On the other hand you get the advantage that the call to the callback can be inlined, as the client code of your (outer) function "sees" the call to the callback will the exact type information being available.

Example for the version with the template parameter (write & instead of && for pre-C++11):

template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}

As you can see in the following table, all of them have their advantages and disadvantages:

function ptrstd::functiontemplate param
can capture context variablesno1yesyes
no call overhead (see comments)yesnoyes
can be inlined (see comments)nonoyes
can be stored in a class memberyesyesno2
can be implemented outside of headeryesyesno
supported without C++11 standardyesno3yes
nicely readable (my opinion)noyes(yes)

Understanding function pointers and reference

The types instantiated via alias templates type0 through type3 all exist. You can't have objects of a function type, though, i.e., there are no instances of the types instantiated via the alias template type0.

There is no "reference to member"-type, i.e., I don't think type4 works

The types are reasonably easy to understand:

  1. R(Args...) is a function value type (return R and taking Args... as arguments). Essentially, that's the type a function has. C++ doesn't allow values of this type.
  2. R(*)(Args...) is a function pointer type. The parenthesis around the * (and if there is a name, the name as in (*name)) are needed to disambiguate whether the * binds to the return type R (the default) or the function type (when there are parenthesis). When you want to pass a function somewhere, you don't use a function value but a function pointer. You can call a function through a function pointer. You can think of a function pointer to be the address where the function implementation lives although there is no guarantee that this is also how it is implemented.
  3. R(&)(Args...) is a function reference type. The relation between function pointers and function reference is the same as that between pointer and reference: you'll get a function reference if you dereference a function pointer.
  4. R(C::*)(Args...) is a member function pointer type. That is essentially a handle for a member function. These are more like indices into lists of member functions but identify a member function. With an object o and a member function pointer mem you can call the member function using something like (o.*mem)(args...). If the member function used to initialize the pointer to member is virtual dynamic dispatch will happen (I don't think there is a way to prevent dynamic dispatch when calling though a member function pointer).

In addition to those function declarations listed the member function pointer type can have const/volatile and/or ref-qualification and there are variable argument versions of the function type. That is, there are also

  • pointer to const members:

    template <class R, class C, class... Args>
    using mem_const = R(C::*)(Args...) const`
  • pointer to volatile members:

    template <class R, class C, class... Args>
    using mem_volatile = R(C::*)(Args...) volatile
  • pointer to const volatile members:

    template <class R, class C, class... Args>
    using mem_const_volatile = R(C::*)(Args...) const volatile
  • pointer to lvalue qualified members:

    template <class R, class C, class... Args>
    using mem_lvalue = R(C::*)(Args...) &;
  • pointer to rvalue qualified members:

    template <class R, class C, class... Args>
    using mem_rvalue = R(C::*)(Args...) &&;
  • all combinations of const/volatile and lvalue/rvalue qualification.

  • variable argument list versions of function types (the ...... can alternatively also be written as a ... ... or ..., ...):

    template <class R, class... Args>
    using varargs = R(Args......);
  • All function pointer, reference, and member function pointer types with a trailing variable argument list.

I think this is [currently] a reasonably exhaustive list of functions and member-functions, their qualified version, and their pointer/reference versions. With C++17 also adding exception specifications to the mix, there will also be a noexcept(true) version (in addition to the default noexcept(false) one.



Related Topics



Leave a reply



Submit