Why Function Objects Should Be Pass-By-Value

why function objects should be pass-by-value

In a typical case, a function object will have little or (more often) no persistent state. In such a case, passing by value may no require actually passing anything at all -- the "value" that's passed is basically little or nothing more than a placeholder for "this is the object".

Given the small amount of code in many function objects, that leads to a further optimization: it's often fairly easy for the compiler to expand the code for the function object inline, so no parameters get passed, and no function call is involved at all.

A compiler may be able to do the same when you pass a pointer or reference instead, but it's not quite as easy -- a lot more common that you'll end up with an object being created, its address passed, and then the function call operator for that object being invoked via that pointer.

Edit: It's probably also worth mentioning that the same applies to lambdas, since they're really just function objects in disguise. You don't know the name of the class, but they create a class in the immediately surrounding scope that overloads the function call operator, which is what gets invoked when you "call" the lambda. [Thanks @Mark Garcia.]

Should I pass function objects by value or by reference?

TL;DR: You should use F: Fn() -> () or impl Fn() -> () as an argument.

Fn

As @Bubletan mentioned in their answer, the key point is that Fn is automatically implemented for &F if F implements Fn:

impl<'_, A, F> Fn<A> for &'_ F
where
F: Fn<A> + ?Sized,

The consequence is that:

  • foo(f: impl Fn() -> ()) can be called both with foo(callable) or foo(&callable).
  • foo(f: &impl Fn() -> ()) forces the caller to use foo(&callable) and disallow foo(callable).

In general, it is best to leave the choice to the caller when there is downside for the callee, and therefore the first form should be preferred.

FnMut

The same logic applies to FnMut, which is also automatically implemented for &mut F if F implements FnMut:

impl<'_, A, F> FnMut<A> for &'_ mut F
where
F: FnMut<A> + ?Sized,

Which should therefore also be passed by value in arguments, leaving the choice to the caller as to whether they prefer foo(callable) or foo(&mut callable).

FnOnce

There is the argument of consistency with FnOnce, which can only be passed by value, which again points into the direction of taking arguments of the Fn* family by value.

Does C++ pass objects by value or reference?

Arguments are passed by value, unless the function signature specifies otherwise:

  • in void foo(type arg), arg is passed by value regardless of whether type is a simple type, a pointer type or a class type,
  • in void foo(type& arg), arg is passed by reference.

In case of arrays, the value that is passed is a pointer to the first element of the array. If you know the size of the array at compile time, you can pass an array by reference as well: void foo(type (&arg)[10]).

Is it ok to pass function objects by reference?

Passing function objects by reference is fine, but you should be aware that many C++ algorithms copy function objects, so if you need a library algorithm to respect your state you should pass it as a reference inside your function object:

struct State {
double a, b, x, y;
};
struct Function {
State &state;
explicit Function(State &state): state(state) {}
double operator() (int v, int w) {....}
};
State state;
std::...(..., Function(state), ...)

Also, some library algorithms (e.g. transform) require pre-C++11 that the function object have no side effects i.e. no state whatsoever; this requirement is rarely enforced and is relaxed in C++11.

C++: Reasons for passing objects by value

ALWAYS use pointers when using objects as arguments

No, in C++ always pass by reference, unless your function can be called with nullptr as a valid argument. If the function does not need to modify the argument, pass by const reference.

Passing arguments by value has several uses.

If your function needs to create a copy of the argument it is better to create this copy by passing by value rather than creating a copy within the function. For instance:

void foo( widget const& w )
{
widget temp( w );
// do something with temp
}

Instead use

void foo( widget w )  // copy is made here
{
// operate on w itself
}

Doing this also has the benefit of allowing the compiler to move widget if possible, which is generally more efficient than creating copies.

can somebody explain me what does passing by value and Passing by reference mean in C#?

In simple terms...

"Passing by value" means that you pass the actual value of the variable into the function. So, in your example, it would pass the value 9.

"Passing by reference" means that you pass the variable itself into the function (not just the value). So, in your example, it would pass an integer object with the value of 9.

This has various consequences, and each is useful in different situations.

This answer has more thorough information:
What's the difference between passing by reference vs. passing by value?

Advantages of pass-by-value and std::move over pass-by-reference

  1. Did I understand correctly what is happening here?

Yes.


  1. Is there any upside of using std::move over passing by reference and just calling m_name{name}?

An easy to grasp function signature without any additional overloads. The signature immediately reveals that the argument will be copied - this saves callers from wondering whether a const std::string& reference might be stored as a data member, possibly becoming a dangling reference later on. And there is no need to overload on std::string&& name and const std::string& arguments to avoid unnecessary copies when rvalues are passed to the function. Passing an lvalue

std::string nameString("Alex");
Creature c(nameString);

to the function that takes its argument by value causes one copy and one move construction. Passing an rvalue to the same function

std::string nameString("Alex");
Creature c(std::move(nameString));

causes two move constructions. In contrast, when the function parameter is const std::string&, there will always be a copy, even when passing an rvalue argument. This is clearly an advantage as long as the argument type is cheap to move-construct (this is the case for std::string).

But there is a downside to consider: the reasoning doesn't work for functions that assign the function argument to another variable (instead of initializing it):

void setName(std::string name)
{
m_name = std::move(name);
}

will cause a deallocation of the resource that m_name refers to before it's reassigned. I recommend reading Item 41 in Effective Modern C++ and also this question.

Where should I prefer pass-by-reference or pass-by-value?

There are four main cases where you should use pass-by-reference over pass-by-value:

  1. If you are calling a function that needs to modify its arguments, use pass-by-reference or pass-by-pointer. Otherwise, you’ll get a copy of the argument.
  2. If you're calling a function that needs to take a large object as a parameter, pass it by const reference to avoid making an unnecessary copy of that object and taking a large efficiency hit.
  3. If you're writing a copy or move constructor which by definition must take a reference, use pass by reference.
  4. If you're writing a function that wants to operate on a polymorphic class, use pass by reference or pass by pointer to avoid slicing.


Related Topics



Leave a reply



Submit