3

My question is what is the advantage of perfect forwarding a function, which is passed to our wrapper.

template<typename T, typename ...U>
    auto time_function(T&& func, U&& ...args)
{
    std::cout<<"timing void function"<<std::endl;
    //std::forward<T>(func)(std::forward<U>(args)...); //this vs next one
    func(std::forward<U>(args)...);

    std::cout<<"timing over"<<std::endl;
}

In the case of argument forwarding, it is clear that forwarding preserves the lvalueness vs rvalueness of the argument. However, is there any use to forward func before calling?

Let's say I pass both temporary functors and normal functions to the time_function wrapper.

L. F.
  • 19,445
  • 8
  • 48
  • 82
Sridhar Thiagarajan
  • 580
  • 1
  • 7
  • 20

2 Answers2

4

Suppose that I have a stateful assignor functor. The assignor will be reused multiple times, so it has to copy the value each time. However, if the assignor is an rvalue, it can just move the value:

struct Assignor {
    std::string value;
    void operator()(std::string& dest) const &
    {
        dest = value;
    }
    void operator()(std::string& dest) &&
    {
        dest = std::move(value);
    }
};

Now, perfect forwarding makes a difference on rvalues:

Assignor f{std::string(10000, 'X')};
std::string a, b, c;

time_function(f, a);                                 // copies the string
time_function(std::move(f), b);                      // should move the string
                                                     // but copies if you don't std::forward
time_function(Assignor{std::string(10000, 'Y')}, c); // same

(This is just an example of how a functor can be optimized w.r.t. value category. I know it looks a bit artificial, but people always come up with creative ideas.)


By the way, you should be using std::invoke instead of directly calling ():

std::invoke(std::forward<T>(func), std::forward<U>(args)...);
JeJo
  • 30,635
  • 6
  • 49
  • 88
L. F.
  • 19,445
  • 8
  • 48
  • 82
  • I am not familiar with const & and && after a functor, we generally use const on a member function to indicate that self object is not modified by this member function. What does const & and && mean in this context? – Sridhar Thiagarajan Nov 04 '19 at 01:22
  • 1
    @SridharThiagarajan Think of it this way: `f(x)` and `f(X{})` call the global functions `void f(const X&);` and `void f(X&&);` respectively. Analogously, `x.f()` and `X{}.f()` call the member functions `void f() const &;` and `void f() &&;` respectively. – L. F. Nov 04 '19 at 09:09
2

In addition to L.F.'s answer I want to make a small note about a potential problem, that is obvious but can still be overlooked: sometimes it is dangerous to pass functional objects by a universal reference and invoke them as rvalues. Suppose that instead of

void time_function(T&& func, ...)

that invokes std::forward<T>(func)(...) once, you have

void for_each(T&& func, ...)

that potentially invokes std::forward<T>(func)(...) multiple times. Then after the first call, all further calls are not safe. In the L.F.'s example, this corresponds to multiple assignments from a "moved-from" state: after the first assignment, std::string value member will be left in a "valid but unspecified state", and later assignments won't do what they are expected to do (though they won't be UB). If the Assignor doesn't have the && overload of operator(), the problem won't show up.

Evg
  • 25,259
  • 5
  • 41
  • 83