Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good practice for passing a function as a parameter : copy, reference, const reference? [duplicate]

Possible Duplicate:
template pass by value or const reference or…?

What is the good practice in the following for a function taking a function as a parameter :

template<class Function> void test1(Function f);
template<class Function> void test2(Function& f);
template<class Function> void test3(const Function& f);

where the passed function can be a functor, a std::function, a function pointer or a lambda function.

like image 739
Vincent Avatar asked Mar 07 '26 17:03

Vincent


2 Answers

Use universal references and you won't need to think about it:

template<class Function> void test(Function&& f);
like image 105
ecatmur Avatar answered Mar 09 '26 07:03

ecatmur


When an argument is passed to a function template deducing the argument's type, it is an interesting question as to how the argument should be passed. It is worth noting that the exact nature of the use of the argument doesn't matter: Whether the argument is used as a function object, an iterator, a value, etc. is fairly irrelevant to answer. There are four options:

  1. template <typename T> void f(T&& a)
  2. template <typename T> void f(T& a)
  3. template <typename T> void f(T const& a)
  4. template <typename T> void f(T a)

It matters a bit as to what the function template actually does: If all f() does with its argument a is to forward it to another function, you want to pass by universal reference. The called function will sort out the details an reject inappropriate options. Of course, although generally useful, forwarding functions are somewhat boring.

If f() actually does something with the object, we can normally immediately discard two of the options:

  1. Pass by universal reference results in a type of which we don't know if it is a reference or a value. This is pretty useless for anything other than forwarding the type as it either behaves like a value or like reference. Just specifying what the function actually does would be a problematic dance.
  2. Pass by T const& isn't doing us much good either: we got a reference to an object whose life-time we can't control and which we can neither move from nor get it copy/move-elided.

Passing an object by non-const reference can be useful if the object itself gets modified. This is clearly an important part of the contract of the function and an imposed design choice which can't be overridden by a client of the function when taken.

The preferred approach is to take arguments by value. Generally, this makes the definition of the function's behavior much easier and actually keeps the choice whether the type should follow value or reference semantics open for the user! Just because something is passed by value doesn't mean that the objects of interest are also passed by value. Specifically for the case of function object, the standard C++ library even provides a generic adapter giving a value type reference semantics: std::ref().

Of course, interface design is subtle and there will be cases where each one of the options is warranted. However, as a general rule of thumb, I think it is very simple:

  1. Forwarding functions use universal references.
  2. Functions doing something with the arguments use values.

... and, of course, these rules only apply to arguments to function templates whose type is deduced.

like image 40
Dietmar Kühl Avatar answered Mar 09 '26 08:03

Dietmar Kühl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!