为什么此函数产生不正确的值?

时间:2018-11-07 12:58:38

标签: c++ templates stdvector

我有一个简单的函数模板来计算容器的平均值:

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本身的不当使用。我不确定它是否来自编译器警告,该警告与转换可能导致的数据丢失有关,或者我是否将其强制转换错误,或者是否与我通常实现此功能有关。提供链接之前,我已经接受了在此页面上找到的答案。我将保留此问题,以备将来参考和读者使用!除此之外,我非常感谢所提供的链接,因为它确实有助于理解错误在代码中的位置,错误是什么以及引起错误的原因以及如何正确修复此错误,此外还可以接受此页面上的答案。

1 个答案:

答案 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才能按照您想要的方式工作。