I really like @Brian and @Passer By answers; it has the same semantics as expanding a parameter package using an operator. I would like to add a (far-fetched) example to demonstrate what this means.
First change the sum function to use decltype(auto) and forward semantics:
template<typename... Args> decltype(auto) sum(Args&&... args) { decltype(auto) fold = (... + std::forward<Args>(args)); return fold; }
It is still functionally the same, only now sum will definitely return the type that fold is, and fold will take the exact type of summation.
Our warehouse expression is called the unary left fold, and its decomposition will look like this:
((E 1 op E 2 ) op ...) op EN
More specifically, suppose Args can be thought of as an array of length N :
((Args[0] + Args[1]) + ...) + Args[N-1]
Which is basically the same as
Args[0] + Args[1] + ... + Args[N-1]
So, now that we have taken some kind of mysticism from all of this, we donβt need to see much that the type of expression is really just the type of the result of adding all types found on both sides of the + operator.
I can create the following type of Foo , which has some strange adding semantics:
const std::string global = "Global string"; struct Foo{ Foo(int){} }; const std::string& operator +(const Foo&, const Foo&){ return global; } const std::string& operator +(const std::string& _str, const Foo&){ return _str; }
And now, when I call sum for Foo instances, I get const std::string& in return, which is definitely not prvalue:
int main() { std::cout << sum(Foo{10}, Foo{2}, Foo{2}) << std::endl; }
Print
Global line