are these functions equivalent?
template <class T>
void foo(T && t)
{
bar(std::forward<T>(t));
}
template <class T>
void foo2(T && t)
{
bar(std::forward<decltype(t)>(t));
}
template <class T>
void foo3(T && t)
{
bar(std::forward(t));
}
if they are, can I always use this macro for perfect forwarding?
#define MY_FORWARD(var) std::forward<decltype(var)>(var)
or just use
bar(std::forward(t));
I believe foo2
and foo3
are same, but I found people are always use forward like foo
, is any reason to explicitly write the type?
I understand that T
and T&&
are two different types, but I think std::forward<T>
and std::forward<T&&>
always give the same result?
Edit:
the reason I want to use macro is I want to save some typing on following C++1y code, I have many similar code in different places
#define XLC_FORWARD_CAPTURE(var) var(std::forward<decltype(var)>(var))
#define XLC_MOVE_CAPTURE(var) var(std::move(var))
template <class T, class U>
auto foo(T && func, U && para )
{
auto val = // some calculation
return [XLC_FORWARD_CAPTURE(func),
XLC_FORWARD_CAPTURE(para),
XLC_MOVE_CAPTURE(val)](){
// some code use val
func(std::forward<U>(para));
};
}
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.
std::forward This is a helper function to allow perfect forwarding of arguments taken as rvalue references to deduced types, preserving any potential move semantics involved.
Are these functions two equivalent?
Yes, they are equivalent. decltype(t)
is the same as T&&
, and when used with std::forward
, there is no difference between T
and T&&
, regardless what T
is.
Can I always use this macro for perfect forwarding?
Yes, you can. If you want to make your code unreadable and unmaintainable, then do so. But I strongly advise against it. On the one hand, you gain basically nothing from using this macro. And on the other hand, other developers have to take a look at the definition to understand it, and it can result in subtle errors. For example adding additional parentheses won't work:
MY_FORWARD((t))
In contrast, the form with decltype
is perfectly valid. In particular, it is the preferred way of forwarding parameters from generic lambda expressions, because there are no explicit type parameters:
[](auto&& t) { foobar(std::forward<decltype(t)>(t)); }
I ignored the 3rd variant with std::forward(t)
, because it isn't valid.
Update: Regarding your example: You can use call-by-value instead of call-by-reference for the function template foo
. Then you can use std::move
instead of std::forward
. This adds two additional moves to the code, but no additional copy operations. On the other hand, the code becomes much cleaner:
template <class T, class U>
auto foo(T func, U para)
{
auto val = // some calculation
return [func=std::move(func),para=std::move(para),val=std::move(val)] {
// some code use val
func(std::move(para));
};
}
The accepted answer does not solve the problem in title completely.
A macro argument preserves the type of the expression. A forwarding parameter in a template does not. This means t
in foo2
(as a forwarding function parameter) has the type T&&
(because this is the forwarding template parameter), but it can be something different when the macro is in other contexts. For example:
using T = int;
T a = 42;
T&& t(std::move(a));
foo(MY_FORWARD(t)); // Which foo is instantiated?
Note here t
is not an xvalue, but an lvalue. With std::forward<T>(t)
, which is equivalent to std::forward<int>(t)
, t
would be forwarded as an lvalue. However, with MY_FORWARD(t)
, which is equivalent to std::forward<int&&>(t)
, t
would be forwarded as an xvalue. This contextual-dependent difference is sometime desired when you have to deal with some declared variables with rvalue reference types (not forwarding paramter even they may look like similar in syntax).
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