Why is std::move necessary when forwarding values through std::tuple?
18:39 14 Aug 2026

Suppose, I want to call function Foo(auto&&...), but indirectly in a deferred way. I would store the function arguments in a std::tuple, then std::apply it on Foo, like in the following example (live):

#include 
#define FWD(...)    std::forward(__VA_ARGS__)


void Foo(auto&&...);
const auto foo = [](auto&&... args) { Foo(FWD(args)...); };


template 
class DeferredFoo {
public:
    DeferredFoo(Args&&... args) : mTuple(FWD(args)...) {}

    void operator()() {
        std::apply(foo, std::move(mTuple));     // why std::move?
    }

private:
    std::tuple mTuple;
};


template 
DeferredFoo(Args&&...) -> DeferredFoo;


int main()
{
    Foo(123);               // direct call
    DeferredFoo(123)();     // indirect call
}

When DeferredFoo finally calls Foo it should forward all ctor. arguments keeping their cv-qualifiers and value category. I achieved what I wanted (see above), but one thing surprised me.

  • Why is std::move necessary on mTuple?

This is a theoretical question. In other words, what would go wrong if std::apply were designed in such a way that std::move were not necessary?

c++ apply perfect-forwarding stdtuple forwarding-reference