6

我最近一直在玩 C++11,并想出了以下 sum 函数:

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?有没有特定的原因?

4

3 回答 3

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;
}
于 2012-04-12T13:21:31.643 回答
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)

即使带有 -Weverything 的 clang 也不会对此发出警告(编辑:虽然我无法使用 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!
                                                  ^~~~
于 2012-04-12T13:28:00.180 回答
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;
}

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

如果您将函子限制为std::function<Sig>. 这仅取决于实现的编写方式std::function,何时发出缩小转换的警告,甚至是否对自己的代码发出警告。

于 2012-04-12T14:20:06.827 回答