MSVC中的折叠表达

时间:2018-02-04 15:09:43

标签: c++ language-lawyer variadic-templates c++17 fold-expression

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

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

使用VS 2017 15.6.0预览3以下代码

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

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

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

2 个答案:

答案 0 :(得分:10)

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

(使用GCCclang编译好,但在MSVC中触发所有四个static_assert并在内部提交。

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

答案 1 :(得分:4)

有趣的问题。

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

我想这是因为在草案n4659 for C ++ 17(对不起:我没有访问最终版本)我看到表达式规则(A.4),其中除法运算符涉及“< em> multiplicative-expression “规则如下

  

乘法表达式 / pm-expression

multiplicative-expression ”也可以是“ pm-expression ”,可以是“ cast-expression ”,可以是一个“一元表达式”,它可以是一个“ postfix-expression ”,它可以是一个“ primary-expression ”,可以是一个“折叠表达

因此规则可以视为

  

fold-expression / pm-expression

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

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

  

fold-expression 的实例化产生:

     

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

和8.1.6

  

表单上的表达式(... op e)其中 op fold-operator ,称为一元左折

所以我认为

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

应该实例化

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

无论如何......对于MSVC还是没有......确定......你想要

return (1 + 3) / 2.0;

我建议您添加另外两个括号。