10

I have the following function that computes the mean value:

template<typename... Ts>
auto mean_of(const Ts... values)
{
    return (... + values) / static_cast<double>(sizeof...(Ts));
}

With VS 2017 15.6.0 Preview 3 the following code

std::cout << mean_of(1, 3);

outputs 2.5. It seems that MSVC interprets the fold expression as 1 + 3 / N and not as (1 + 3) / N. If I add extra parentheses around the fold expression, the result is correct. With GCC no extra parentheses are needed.

Is this a bug in MSVC or do we need extra parentheses?

max66
  • 65,235
  • 10
  • 71
  • 111
Evg
  • 25,259
  • 5
  • 41
  • 83
  • 2
    A related confusion: What's the behavior of `decltype(x op ...)` with one argument for the function parameter pack? http://coliru.stacked-crooked.com/a/20448cc227c873fa – aschepler Feb 04 '18 at 16:37
  • @aschepler my take on this very clever case is that since a fold expression is not (grammatically speaking) an *id-expression* then [`decltype` should follow non-entity rules](http://eel.is/c++draft/dcl.type.simple#4.2) regardless of the effective meaning of the expansion – Luc Danton Feb 06 '18 at 05:24
  • @aschepler [a `sizeof` variant](http://coliru.stacked-crooked.com/a/4578bc442d653bdb) to possibly showcase [a \[temp.variadic\] rule](http://eel.is/c++draft/temp.variadic#5) without the same grammar pitfall – Luc Danton Feb 06 '18 at 05:26
  • [yet another variant](http://coliru.stacked-crooked.com/a/82b018d948324855), perhaps closer in spirit to the original – Luc Danton Feb 06 '18 at 06:37
  • @LucDanton Neither of those are really as surprising to me. The differences happen only because the built-in `+` coerces its subexpressions to prvalues and results in a prvalue, but if the number of `+` operators in the expansion is zero, that doesn't apply. And we couldn't have a rule like "the type and value category of a unary fold with one argument are what they would be for multiple arguments", because in general each instance of the operator could turn out to be an overloaded operator call, which could result in any value category and any conceivable type. – aschepler Feb 06 '18 at 22:45
  • @aschepler [there is a precedent](http://eel.is/c++draft/temp.variadic#7.2) to having rules for avoiding semantic surprises as you put it (even when that rule is syntactical in nature), and we can certainly choose to have any rule we want :) (also note that I’m just riffing on your own demo, they're all constructed the same way) – Luc Danton Feb 06 '18 at 23:41

2 Answers2

10

This is a bug in MSVC. I've reduced it to:

template<class... Ts>
constexpr auto f1(Ts const... vals) {
    return 0 * (vals + ...);
}

template<class... Ts>
constexpr auto f2(Ts const... vals) {
    return (vals + ...) * 0;
}

static_assert(f1(1,2,3) == 0);
static_assert(f1(1,2,3) != 0 * 1 + (2 + 3));
static_assert(f2(1,2,3) == 0);
static_assert(f2(1,2,3) != 1 + (2 + 3) * 0);

(which compiles fine with both GCC and clang, but triggers all four static_asserts in MSVC) and filed it internally.

20180205 Update: This bug has been fixed for a future release of Visual C++.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • In the yesterday's release 15.6.0 Preview 4 it is not fixed. Probably, will be fixed later. – Evg Feb 08 '18 at 06:58
4

Interesting question.

Correcting my first interpretation, it seems to me that is g++ and clang++ are right and that MSVC is wrong.

I suppose this because in the draft n4659 for C++17 (sorry: I don't have access at the final version) I see the expression rules (A.4) where the division operator is involved in a "multiplicative-expression" rule as follows

multiplicative-expression / pm-expression

A "multiplicative-expression" can be also a "pm-expression" that can be a "cast-expression" that can be an "unary-expression" that can be a "postfix-expression" that can be a "primary-expression" that can be a "fold-expression"

So the rule can be seen as

fold-expression / pm-expression

So, If I'm not wrong, a "fold-expression" should be evaluated as a whole before the division is applied.

My first interpretation (MSVC right, g++ and clang++ wrong) was based over an hasty lecture of 17.5.3

The instantiation of a fold-expression produces:

(9.1) ((E1 op E2) op ···) op EN for a unary left fold

and 8.1.6

An expression on the form (... op e) where op is a fold-operator is called a unary left fold.

So I supposed that

return (... + values) / static_cast<double>(sizeof...(Ts));

should be instantiated

return ((v1 + v2) + ... ) + vn / static_cast<double>(sizeof...(Ts));

Anyway... right MSVC or not... to be sure... of you want

return (1 + 3) / 2.0;

I suggest you to add another couple of parentheses.

Community
  • 1
  • 1
max66
  • 65,235
  • 10
  • 71
  • 111
  • 3
    Interesting interpretation, but on the other hand the syntax says that the entire fold-expression is a subexpression of the division expression... – aschepler Feb 04 '18 at 16:32
  • @Rakete1111 - yes: it's counter-intuitive for me too; hoping to be wrong, if I'm right I see it as a defect. – max66 Feb 04 '18 at 16:37
  • @aschepler - I suppose this can invalidate my interpretation; can you give me a reference? – max66 Feb 04 '18 at 16:39
  • 2
    Nothing specific - it's just that parsing using a grammar generates a specific syntax tree, and you don't usually rearrange that tree due to semantics. But I do agree the Standard is unclear: does "the instantiation produces" mean there is a replacement of tokens and syntactic reparsing, or just that the semantics of the expression are as though it had been this expression instead? – aschepler Feb 04 '18 at 16:46
  • @aschepler - I also see that a "fold expression" is defined as "primary-expression" exactly as "( expression )". But this say us that a "fold expression" can be seen as an expression surrounded by parentheses? I hope so, because I find really disturbing my interpretation, but I don't see nothing explicit. I'm really confused. – max66 Feb 04 '18 at 16:57
  • @aschepler - paraphrasing Groucho Marx, if you don't like my interpretation, I have another one. Answer modified. What do you think? – max66 Feb 04 '18 at 17:37
  • @max66 The parentheses in a *fold-expression* are mandatory! Which in turn means that `(... + values)` cannot be parsed as a *fold-expression* inside of a parenthesized *expression* (not without extra parens, that is). Also, according to expr.prim.fold, `(... + values)` is a left fold (because `+` is a *fold-operator* and `values` is an unexpanded parameter pack). A left fold has a straightforward mathematical definition, so I think your new interpretation is correct in any case. MSVC can't just add new operations into the fold, it may only plug the fold's root into another computation. – Arne Vogel Feb 05 '18 at 09:36
  • 2
    @ArneVogel - exactly: the fact that the external parentheses are a mandatory part of a *fold-expression*, but externally absent in the correspondent production, is the point that drove me to my first (now I suppose wrong) interpretation. – max66 Feb 05 '18 at 12:32
  • 5
    If MSVC was right here, the standard would be wrong. – Yakk - Adam Nevraumont Feb 05 '18 at 13:28