You must use std::forward in order to preserve the value category of the argument(s) to fn(). Since the arguments have a name within fn, they are lvalues, and without std::forward they will always be passed as such to std::forward_as_tuple. 
The difference can be demonstrated using the following example:
template<typename T>
void bar2(T&& t)
{
    std::cout << __PRETTY_FUNCTION__ << ' '
               << std::is_rvalue_reference<decltype(t)>::value << '\n';
}
template<typename T>
void bar1(T&& t)
{
    std::cout << __PRETTY_FUNCTION__ << ' '
              << std::is_rvalue_reference<decltype(t)>::value << '\n';
    bar2(std::forward<T>(t));
    bar2(t);
}
bar1 always passes it arguments on to bar2, once with std::forward and once without. Now let's call them with an lvalue and an rvalue argument.
foo f;
bar1(f);
std::cout << "--------\n";
bar1(foo{});
Output:
void bar1(T&&) [with T = foo&] 0
void bar2(T&&) [with T = foo&] 0
void bar2(T&&) [with T = foo&] 0
--------
void bar1(T&&) [with T = foo] 1
void bar2(T&&) [with T = foo] 1
void bar2(T&&) [with T = foo&] 0
As you can see from the output, in both cases, without the use of std::forward, the argument is being passed as an lvalue to bar2.