Is the value category of a fold expression always prvalue?

Is the fold expression always prvalue? Is it correct?

template<typename... Args> auto sum(Args... args) { auto fold = (... + args); return fold; } int main() { sum(10, 2, 2); } 

I'm really only interested in the fold expression, which (... + args) in the example above.

+5
source share
4 answers

The bending expression has the same semantics as a simple statement of the applications of the N - 1 operator (where N is the number of elements in the package). For example, sum(10, 2, 2) will create (10 + 2) + 2 . See [temp.variadic] / 9 .

In general, it may or may not be a prvalue. Adding 2 or more numeric values ​​with + will always give a prvalue value, because the built-in + operator gives a prvalue value, but if there is only one element in the args package, then (... + args) means the same as just mentioning that one element by its hypothetical name, so the result will be lvalue. And, of course, you can also reset with other (possibly overloaded) operators, which can cause glvalues.

+7
source

Not necessary. It all depends on the operator. If you used instead of += (somewhat far-fetched use), the resulting expression would not be prvalue, but lvalue.

To clarify:

 template<typename... Args> auto sum(Args... args) { auto fold = (... += args) = 3; return fold; } 

Since the result of += is an lvalue denoting the left operand (I just discount the crazy overloads for simplicity), the fold statement also completes the creation of the lvalue. Thus, the appointment at the end is perfectly acceptable, although smelly.

+4
source

Fake expressions do not affect the category of expression values; it is literally just a compiler that extends the parameter package for you

 template<typename... Args> void foo(Args&... args) { (args, ...) = 42; } void bar() { int x, y, z; foo(x, y, z); } 

Indeed, since (args, ...) is of type int&

+2
source

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

+2
source

Source: https://habr.com/ru/post/1272327/


All Articles