Reputation: 26292
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
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_assert
s in MSVC) and filed it internally.
20180205 Update: This bug has been fixed for a future release of Visual C++.
Upvotes: 10
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