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?
So let us understand first what are fold expressions. This is a new feature in the C++ 17 compiler. It usually allows a user to apply the same set of binary operations to all the arguments. It works in the sequence of the first 2 arguments, then to the third, and so on… <bin_operation>args4) …… and so on.
With fold expressions, you can implement Haskell known functions foldl, foldr, foldl1 and foldr1 directly in C++. These four functions successively reduce a list to a single value. C++11 supports variadic templates. These are templates that can accept an arbitrary number of template arguments. The arbitrary number is held by a parameter pack.
The instantiation of a fold expression expands the expression e as follows: template<typename ... Args> bool all ( Args... args) { return ( ... && args); } bool b = all (true, true, true, false); // within all (), the unary left fold expands as // return ( (true && true) && true) && false; // b is false
With fold expressions, you can directly reduce the parameter pack with the help of the binary operator. Here is the output of the program. Now to the two variations of fold expression that result in four different forms of fold expressions. At first, fold expression can have a default value. That value depends on the binary operator.
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++.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With