0

I am wondering why std::apply does not forward rvalue-reference when working with reference types in tuples (see Live):

#include <type_traits>
#include <tuple>

template<typename T>
void assertRvalueReference(T&& t)
{   
    static_assert(std::is_rvalue_reference_v<decltype(t)>, "Ups"); 
}

int main()
{
    struct A{};
    A a;
    int v;
    auto t = std::tuple<A&&, int>(std::move(a), v); // essentially `std::forward_as_tuple(v, std::move(a))`

    std::apply([](auto&& arg, ...)
               {
                   assertRvalueReference(std::forward<decltype(arg)>(arg)); 
               }, std::move(t));

    std::apply([](auto&& arg, ...)
               {
                   // assertRvalueReference(arg); // This should in my opinion not fail
               }, t);

    assertRvalueReference(non_std_get<0>(t)); 
}

The root cause for this is std::get and the reference collapsing rule. Wouldn't it make more sense if std::apply internally would use this non_std_get


template<std::size_t Index, typename Tuple>
constexpr decltype(auto) non_std_get(Tuple&& t)
{
    using Type = std::tuple_element_t<Index, std::remove_cvref_t<Tuple>>;
    if constexpr(std::is_rvalue_reference_v<Type>)
    {
        return std::move(std::get<Index>(t));
    }
    else
    {
        return std::get<Index>(t);
    }
}

which would result in a perfect forward for

std::apply([](auto&& arg){/*arg is here `int&&`*/}, t);
Gabriel
  • 8,990
  • 6
  • 57
  • 101
  • 2
    An rrvalue--reference is an lvalue. To make it behave differently in this specific case would be confusing. – super Jan 03 '20 at 13:08
  • Thats true, but for what use is the `std::apply(t,...)` `int a; auto t = std::forward_as_tuple(a, A{})` when its not doing a perfect forward, meaning the lambda should get a `rvalue-reference = A&&` (which it does not, it gets a `A&`) – Gabriel Jan 03 '20 at 13:13
  • It makes sense to me since passing an rvalue-reference to a function is in fact passing an lvalue to a function. I don't want code to magically do hiddens things for me without me telling it to do so. In that case I would prefer a `apply_with_forwarding` to be able to be explicit in my code. – super Jan 03 '20 at 13:19

1 Answers1

0

The problem is not in std::apply, it is in your lambda.

Inside your lambda, arg is always an lvalue. You need to explicitly cast it to an rvalue with std::forward in order to make it an rvalue.

Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
  • your statement is clear to me, the real issue is in `std::get` which returns a `A&` if the **lvalue** tuple contains a type `A&&` = rvalue-reference. – Gabriel Jan 03 '20 at 14:42