我有一个简单的函数模板来计算容器的平均值:
template<typename T>
T array_average( std::vector<T>& values ) {
if( std::is_arithmetic<T>::value ) {
if( !values.empty() ) {
if( values.size() == 1 ) {
return values[0];
} else {
return (static_cast<T>( std::accumulate( values.begin(), values.end(), 0 ) ) / static_cast<T>( values.size() ) );
}
} else {
throw std::runtime_error( "Can not take average of an empty container" );
}
} else {
throw std::runtime_error( "T is not of an arithmetic type" );
}
}
我在上面的static_cast<>
中进行了添加,以尝试将计算强制为所需的类型<T>
。
当我使用uint64_t
调用main中的此功能时
std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
std::cout << array_average( values ) << '\n';
此代码确实会产生MSVC的编译器警告C4244
,因为转换可能会导致数据丢失,但是它运行正常,这给了我预期的结果,并且将6
打印到控制台。这是正确的,因为实际值是6.5
,但是由于整数除法6
的截断是正确的。
现在,如果我改用上面的函数:
std::vector<double> values { 2.0, 3.5, 4.5, 6.7, 8.9 };
std::cout << array_average( values2 ) << '\n';
这应该给我一个5.12
的结果,但是它显示的是4.6
。这也给了我与上面相同的编译器警告,但是它运行时没有运行时错误(执行中断),但给出的结果不正确。
我不确定函数中的错误在哪里。我不知道这是否是由于该编译器警告引起的,还是我设计函数本身的方式。
-编辑-
用户建议这可能是该Q/A的重复,我不能反对它确实回答了我的问题这一事实。在问这个问题时;我不知道该错误来自std::accumulate
本身的不当使用。我不确定它是否来自编译器警告,该警告与转换可能导致的数据丢失有关,或者我是否将其强制转换错误,或者是否与我通常实现此功能有关。提供链接之前,我已经接受了在此页面上找到的答案。我将保留此问题,以备将来参考和读者使用!除此之外,我非常感谢所提供的链接,因为它确实有助于理解错误在代码中的位置,错误是什么以及引起错误的原因以及如何正确修复此错误,此外还可以接受此页面上的答案。
答案 0 :(得分:18)
您的static_cast
放在错误的位置。您正在转换累积的结果,但是让累积以初始项的类型(此处为0
,即int
)运行。而是这样做:
return std::accumulate( values.begin(), values.end(), static_cast<T>(0) ) / static_cast<T>( values.size() );
(请注意4.6确实是static_cast<double>(2 + 3 + 4 + 6 + 8) / 5.0
的结果。)
与问题核心无关的评论:
const std::vector<T>&
,因为它不会修改values
。T
无效的std::accumulate
调用函数(例如,不是算术运算符),则会出现编译时错误。最顶端的if
必须是if constexpr
才能按照您想要的方式工作。