编译器没有警告精度损失?

时间:2012-04-12 12:58:27

标签: c++ c++11 g++ compiler-warnings

我最近一直在玩C ++ 11,并提出了以下求和函数:

template <typename T>
inline T sum(const std::function<T (int)> &f, int initial, int end)
{
    T retval = 0;
    for(int k = initial; k <= end; k++) {
        retval += f(k);
    }
    return retval;
}

我的想法是,我可以传递一个lambda函数,因此对数学和有一个整洁可读的函数。然后我尝试了以下内容:

int main()
{
    std::array<double, 2> arr1 = {{ 0.5, 1.5 }},
                          arr2 = {{ 1.5, -0.5 }};
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss!
                      0, 1);
    return 0;
}

我使用g ++ 4.6.3编译了这个:g++ -Wall -pedantic -std=c++0x -o test main.cpp并且没有对我在源代码的注释中提到的精度损失发出任何警告。

这里给出的sum<int>是一件坏事,但在更复杂的情况下可能并不那么明显。编译器是否应该注意到我的lambda函数的返回值是double,并警告我在转换为int时我的精度会丢失?它有没有特定的原因?

3 个答案:

答案 0 :(得分:5)

似乎完全合理。您告诉编译器不要打扰模板参数扣除(即使用sum<double>),而是明确告诉它使用sum<int>。这和明确的演员一样好。

[编辑]

怎么样?
template <typename F>
auto sum(F const& f, int initial, int end) -> decltype(f(initial))
{
    auto retval = f(initial++);
    while(initial <= end) {
        retval += f(initial++);
    }
    return retval;
}

答案 1 :(得分:2)

VS11会发出警告:

警告1警告C4189:'n':局部变量已初始化但未引用 警告2警告C4244:'+ =':从'double'转换为'int',可能会丢失数据

编辑,实际上该警告来自使用代码:

template <typename T,typename Func>
inline T sum(Func f, int initial, int end)

如果您使用std::function<T (int)>,则会收到有关错误转化的其他警告,因此VS在这个问题上仍然很好。 (IMO,你通常应该将仿函数作为模板类型而不是std :: function)

甚至叮叮当当 - 我们都没有发出关于此的警告(编辑:虽然我无法用clang ATM测试std :: function版本)。似乎可以改进的东西。

我确实得到了这个奇怪的警告:

ConsoleApplication1.cpp:15:51: warning: will never be executed [-Wunreachable-code]
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss!
                                                  ^~~~

答案 2 :(得分:1)

如果您取消了仿函数,并且您将行retval += f(k);更改为retval += T { f(k) };,请按以下方式进行:

// Machinery to allow caller to indifferently use
// sum(f, i, j) and sum<R>(f, i, j)
struct deduced {};

template<
    typename Request = deduced
    , typename Functor
    , typename Ret = typename std::conditional<
        std::is_same<Request, deduced>::value
        , typename std::result_of<Functor&(int)>::type
        , Request
    >::type
>
inline Ret sum(Functor f, int initial, int end)
{
    Ret retval = 0;
    for(int k = initial; k <= end; k++) {
        retval += Ret { f(k) };
    }
    return retval;
}

然后不依赖于编译器的警告意愿,而是让 required 发出诊断,因为在列表初始化(即使用大括号初始化)中不允许缩小转换。

如果您将仿函数约束为std::function<Sig>,我认为没有可靠的方法。这完全取决于实现如何编写std::function,何时发出缩小转换的警告,甚至是否警告自己的代码。