Evg
Evg

Reputation: 26292

Fold expressions in MSVC

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?

Upvotes: 9

Views: 1827

Answers (2)

Casey
Casey

Reputation: 42554

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++.

Upvotes: 10

max66
max66

Reputation: 66200

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.

Upvotes: 4

Related Questions