我有以下函数来计算平均值:
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中的错误还是需要额外的括号?
答案 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);
(使用GCC和clang编译好,但在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;
我建议您添加另外两个括号。