Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

On what base fold expression of a parameter pack consisting of a single element is transformed into unparenthesized expression

Consider an example:

#include <type_traits>

template <class... Ts>
decltype (auto) foo(Ts... ts) {
      return (ts->x + ...);
}

struct X {
    int x;
};

int main() {
    X x1{1};
    static_assert(std::is_reference_v<decltype(foo(&x1))>);
}

[live demo]

decltype(auto) deduced from parenthesized lvalue should according to [cl.type.simple]/4.4 be deduced to lvalue reference. E.g.:

decltype(auto) foo(X *x) { // type of result == int&
    return (x->x);
}

But the snipped triggers static_assert. Even if we compose expression into additional parentheses, e.g.:

return ((ts->x + ...));

It doesn't change the effect.

Is there a point in the standard that prevents deduction of a fold-expression of a single element into the lvalue reference?


Edit

As a great point of Johannes Schaub - litb clang actually does interpret the double-parens-version of the code as parenthesized lvalue and deduce lvalue reference. I'd interpret it as a gcc bug in this case. The version with single-parens-version is however still under the question. What puzzles me is that the version must be transformed into a parenthesized code at least in case of more than one element - to fulfil operator precedence. E.g.:

(x + ...)*4 -> (x1 + x2)*4

What is the reason of the inconsistency?

like image 968
W.F. Avatar asked Oct 31 '17 11:10

W.F.


1 Answers

If you want a reference to be returned in the single parameter case, you'll need an extra set of parenthesis* so that the parameter pack expansion expands references:

template <class... Ts>
decltype (auto) foo(Ts... ts) {
      return ((ts->x) + ...);
}

Unfortunately in the scenario of multiple arguments being passed, the result is still the summation of integers, which returns an rvalue, so your static assertion will fail then.


Why doesn't ((ts->x + ...)) evaluate to a reference?

Because the fold expression will return an expiring int and then wrapping that int in another level of parentheses is still an int. When we use the inner parentheses ((ts->x) + ...)) the fold expression for a single argument returns int&. It does for clang 6.0.0 but not gcc 8.0.0, so I'm not sure.

*The fold expression's parentheses (the parentheses involved in (ts->x + ...)) are required as part of the fold expression; saying ts->x + ... alone is ill-formed.

like image 193
AndyG Avatar answered Sep 29 '22 00:09

AndyG