A code snippet I saw in Effective Modern C++ has a clever implementation of the instrumentation rationale to create a function timer :
auto timeFuncInvocation =
[](auto&& func, auto&&... params)
{
start timer;
std::forward<decltype(func)>(func)(
std::forward<decltype(params)>(params)...);
stop timer and record elapsed time;
};
My question is about std::forward<decltype(func)>(func)(...
This looks like a good use case for the use of familiar template syntax in lambda expressions in case we wanted to make the timer type a compile time constant.
We use std::forward to retrieve the original value category. If for the handler we provided an rvalue then insert will move from it. If for the handler we provided an lvalue then insert will copy it. There are a lot of rules that come into play for the initial deceivingly simple code.
Perfect forwarding reduces excessive copying and simplifies code by reducing the need to write overloads to handle lvalues and rvalues separately. Note: The function the arguments are forwarded to can be a normal function, another template function, or a constructor.
Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions (via pointers thereto), lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
std::move takes an object and casts it as an rvalue reference, which indicates that resources can be "stolen" from this object. std::forward has a single use-case: to cast a templated function parameter of type forwarding reference ( T&& ) to the value category ( lvalue or rvalue ) the caller used to pass it.
A better description of what std::forward<decltype(func)>(func)(...)
is doing would be preserving the value category of the argument passed to the lambda.
Consider the following functor with ref-qualified operator()
overloads.
struct foo
{
void operator()() const &&
{ std::cout << __PRETTY_FUNCTION__ << '\n'; }
void operator()() const &
{ std::cout << __PRETTY_FUNCTION__ << '\n'; }
};
Remember that within the body of the lambda func
is an lvalue (because it has a name). If you didn't forward
the function argument the &&
qualified overload can never be invoked. Moreover, if the &
qualified overload were absent, then even if the caller passed you an rvalue foo
instance, your code would fail to compile.
Live demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With