MSVC 中的折叠表达式

Fold expressions in MSVC

我有以下计算平均值的函数:

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

使用 VS 2017 15.6.0 Preview 3 以下代码

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

输出2.5。似乎 MSVC 将折叠表达式解释为 1 + 3 / N 而不是 (1 + 3) / N。如果我在折叠表达式周围添加额外的括号,结果是正确的。使用 GCC 不需要额外的括号。

这是 MSVC 中的错误还是我们需要额外的括号?

有趣的问题。

纠正我的第一个解释,在我看来,g++ 和 clang++ 是正确的,而 MSVC 是错误的。

我想这是因为在 C++17 的 n4659 草案中(抱歉:我无法访问最终版本)我看到表达式规则 (A.4) 其中除法运算符涉及“乘法表达式”规则如下

multiplicative-expression / pm-expression

一个“multiplicative-expression”也可以是一个“pm-expression”,它可以是一个“” cast-expression”可以是“unary-expression”可以是“postfix-expression”一个“primary-expression”可以是一个“fold-expression

所以规则可以看成

fold-expression / pm-expression

所以,如果我没记错的话,“fold-expression”应该在应用除法之前作为一个整体求值。

我的第一个解释(MSVC 正确,g++ 和 clang++ 错误)是基于 17.5.3 的仓促讲座

The instantiation of a fold-expression produces:

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

和 8.1.6

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

所以我认为

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

应该实例化

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

无论如何...是否正确的 MSVC...确定...您想要

return (1 + 3) / 2.0;

我建议你再添加一对括号。

这是 MSVC 中的错误。我已将其缩减为:

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);

(这两个 GCC and clang 都能很好地编译,但会触发 MSVC 中的所有四个 static_assert)并在内部归档。

20180205 更新:此错误已在 Visual C++ 的未来版本中修复。