Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is any difference between std::forward<T> and std::forward<decltype(t)>?

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)); 
          };
}
like image 668
Bryan Chen Avatar asked Apr 27 '14 08:04

Bryan Chen


People also ask

What is the difference between std :: forward and std :: move?

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.

What is std :: forward?

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.


2 Answers

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)); 
    };
}
like image 160
nosid Avatar answered Oct 19 '22 20:10

nosid


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).

like image 2
FrankHB Avatar answered Oct 19 '22 19:10

FrankHB