是否可以在 C++ 中使用模板化的单参数求和函数?
Is it possible to have a templated, single-parameter sum function in C++?
在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
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.
}
此代码几乎 有效。问题是 auto
在 iterable 为空的情况下看到 return 0;
并且它假定函数必须 return 和 int
。然后它看到 float
版本 return 是 float
并且它感到困惑。有没有办法告诉编译器,如果它看到 return
稍后在 returns float
?
上,就说 return float(0)
是的,您至少可以对标准容器进行此操作。
标准容器为存储在该容器中的值类型定义了一个名为 value_type
的类型别名。对于空容器,您可以 return 这种类型的值构造对象:
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;
}
这确实取决于包含的类型是默认构造的,但这可能不是主要问题(当然适用于像 int
和 float
这样的原始类型)。
以下方法甚至适用于非标准容器;只要某些东西以合理的方式实现 begin()
和 end()
。
#include <list>
#include <iostream>
#include <type_traits>
template<typename iterable>
auto sum(iterable && iterable_) {
auto it = iterable_.begin();
auto end = iterable_.end();
typedef typename std::remove_reference<decltype(*it)>::type value_type;
if (it == end) {
return value_type(0);
}
auto res = *(it++);
while (it != end) {
res += *it++;
}
return res;
}
int main() {
std::list<float> float_list = {1.2, 3.4, 9.9};
std::cout << sum(float_list) << '\n'; // works now.
}
如果你想要一些东西与 any C++11 范围一起工作(也就是说,你可以在基于范围的 for 表达式中迭代的任何东西——包括数组作为有空闲 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{});
}
如果您不想换行 accumulate
,那么您也可以重新实现该循环来做同样的事情。
在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
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.
}
此代码几乎 有效。问题是 auto
在 iterable 为空的情况下看到 return 0;
并且它假定函数必须 return 和 int
。然后它看到 float
版本 return 是 float
并且它感到困惑。有没有办法告诉编译器,如果它看到 return
稍后在 returns float
?
return float(0)
是的,您至少可以对标准容器进行此操作。
标准容器为存储在该容器中的值类型定义了一个名为 value_type
的类型别名。对于空容器,您可以 return 这种类型的值构造对象:
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;
}
这确实取决于包含的类型是默认构造的,但这可能不是主要问题(当然适用于像 int
和 float
这样的原始类型)。
以下方法甚至适用于非标准容器;只要某些东西以合理的方式实现 begin()
和 end()
。
#include <list>
#include <iostream>
#include <type_traits>
template<typename iterable>
auto sum(iterable && iterable_) {
auto it = iterable_.begin();
auto end = iterable_.end();
typedef typename std::remove_reference<decltype(*it)>::type value_type;
if (it == end) {
return value_type(0);
}
auto res = *(it++);
while (it != end) {
res += *it++;
}
return res;
}
int main() {
std::list<float> float_list = {1.2, 3.4, 9.9};
std::cout << sum(float_list) << '\n'; // works now.
}
如果你想要一些东西与 any C++11 范围一起工作(也就是说,你可以在基于范围的 for 表达式中迭代的任何东西——包括数组作为有空闲 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{});
}
如果您不想换行 accumulate
,那么您也可以重新实现该循环来做同样的事情。