是否可以在C++中使用模板化的单参数求和函数?

Jon*_*ung 2 c++ templates sum generic-programming

在python中,我们可以这样做:

int_list = [1, 2, 3, 4, 5]
print(sum(int_list)) # prints 15
float_tuple = (1.2, 3.4, 9.9)
print(sum(float_tuple)) # prints 14.5
Run Code Online (Sandbox Code Playgroud)

sum函数接受任何可迭代的元素,这些元素知道如何相互添加并且为0并生成总和.

我想在C++ 11中创建一个相同的函数.我知道存在accumulate方法,但我想要一个带有单个参数的函数.基本上,我想知道如何编译以下代码:

#include <string>
#include <iostream>
#include <vector>
#include <deque>
#include <list>

template<typename iterable>
auto sum(iterable iterable_) {
    auto it = iterable_.begin();
    auto end = iterable_.end();
    if (it == end) {
        return 0;
    }
    auto res = *(it++); 
    while (it != end) {
        res += *it++;
    }
    return res;
}

int main() {
    std::vector<int> int_vector = {0, 1, 2, 3, 4, 5}; 
    std::cout << sum(int_vector) << '\n';    // prints 15
    std::deque<int> int_deque = {4, 5, 7};
    std::cout << sum(int_deque) << '\n';     // prints 16
    std::list<float> float_list = {1.2, 3.4, 9.9};
    std::cout << sum(float_list) << '\n';    // should print 14.5, but produces error.

}
Run Code Online (Sandbox Code Playgroud)

这段代码几乎可行.问题是auto看到return 0;在iterable为空的情况下,它假定函数必须返回一个int.然后它看到float版本返回a float并且它变得混乱.有没有办法告诉编译器,return float(0)如果它看到后者return返回float

Jer*_*fin 5

是的,你可以至少为标准容器做这项工作.

标准容器定义了一个类型别名value_type,该别名以存储在该容器中的值类型命名.对于空容器,可以返回此类型的值构造对象:

template<typename iterable>
auto sum(iterable const &iterable_) {
    auto it = iterable_.begin();
    auto end = iterable_.end();
    if (it == end) {
        return typename iterable::value_type();
    }
    auto res = *(it++); 
    while (it != end) {
        res += *it++;
    }
    return res;
}
Run Code Online (Sandbox Code Playgroud)

这取决于所包含的类型是默认可构造的,但这可能不是一个主要问题(当然适用于像int和这样的原始类型float).

  • 要支持原始数组,您可以使用独立的`std :: begin`和`std :: end`,以及`decltype`而不是`iterable :: value_type()`(原始数组不存在).另外,我只是使用`decltype`直接声明返回类型.然后`返回0;`就足够了.:) (3认同)

Bar*_*rry 5

如果你想要一些东西适用于任何 C++ 11范围(也就是说,你可以在基于范围的表达式中迭代的任何东西 - 包括数组以及具有free begin和的容器end),我们可以添加一些使用 -声明和包装std::accumulate

template <class Range>
auto sum(Range&& range) {
    using std::begin;
    using std::end;
    using T = std::decay_t<decltype(*begin(range))>;

    return std::accumulate(begin(range), end(range), T{});
}
Run Code Online (Sandbox Code Playgroud)

如果您不想换行accumulate,那么您也可以重新实现该循环以执行相同的操作.