Search code examples
c++templatesc++11decltypeperfect-forwarding

Is there a way to write a macro to do perfect forwarding using only the variable name (i.e. a FWD(t) which is equivalent to std::forward<T>(t))?


I have something like (let's say):

template <typename Collection, typename Item>
void foo_collection(Collection&& c, Item && i) {
    foo(std::forward<Collection>(c), std::forward<Item>(i));
}

I don't like the fact that std::forward<Collection>(c) is so long. I'd like to do this, instead:

template <typename Collection, typename Item>
void foo_collection(Collection&& c, Item&& i) {
    foo(FWD(c), FWD(i));
}

I am thinking there must be a way to do this using decltype. I figure, given the decltype(i), if I remove all references and consts from it, I'll get Item, and then it should work:

#define FWD(v) \
    std::forward< \
        typename std::remove_const<\
        typename std::remove_reference<\
        decltype(v)>::type>::type>(v)

However, this doesn't work:

void foo(int& a) { cout << "ref" << endl; }
void foo(const int& a) { cout << "cref" << endl; }

template <typename T>
void call_foo(T&& t) { foo(FWD(t)); }

int main() {
    int a = 10;
    foo(10);      // 1) prints cref
    foo(a);       // 2) prints ref
    call_foo(10); // 3) prints cref
    call_foo(a);  // 4) prints cref
    return 0;
}

Why does 4 call the cref overload, and not the ref overload?

I tried #define FWD(a) std::forward<decltype(a)>(a), and in this example it did work. However, I am guessing that it won't work in all cases. Is that the case, and if so, which cases won't it work in?

Finally, if the latter approach doesn't work, is there any way to write this macro in a way that works?


Solution

  • #define FWD(a) std::forward<decltype(a)>(a)
    

    will work. So long as a is the name of a variable, decltype(a) is the type it was declared as, which is what you are supposed to pass to forward as its type parameter.

    If you pass in an expression, things could get strange. I cannot think up a case where it fails, however.

    Naturally this doesn't work for some more advanced uses of forward.