如何对多维 std::vector 的所有元素求和?

How to sum all the elements of a multi-dimensional std::vector?

本文关键字:元素 求和 vector std      更新时间:2023-10-16

这个想法简单明了:
继续将n维向量分解为n-1维组成向量,直到您可以访问基元数据类型对象。然后将它们全部添加。

问题是,如何推断返回类型?

可以这样完成,但它已经假定求和变量的数据类型(返回类型):

typedef int SumType;
template <class T>
T Sum (const T x)
{
return x;
}
template <class T>
SumType Sum (const std::vector<T>& v)
{
SumType sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}

但我不想像上面那样做。我觉得这违背了元编程的精神。

我们必须通过不断将向量剖析到其组成向量中来推断返回类型,直到我们到达基元数据类型对象,然后选择返回类型作为基元数据类型。

在C++可能吗?(我是元编程的菜鸟)


附言<numeric>
std::accumulate()可能会有所帮助,但它通过从其第三个参数__init推断返回类型来绕过问题。

这可以在没有任何模板元编程的情况下完成。您可以让编译器使用autodecltype推断类型:

template <class T>
T Sum(const T x) {
return x;
}
template <class T>
auto Sum(const std::vector<T> &v) {
decltype(Sum(v[0])) sum = 0;
for (const auto &x : v)
sum += Sum(x);
return sum;
}

Sum的返回类型是从sum自动推导出的,sum的类型是Sum(v[0])返回的任何类型。最终,您将得到返回T的第一个版本的Sum,并且编译器知道该类型。

演示

我们可以做的是使用 T.C. 的data_type类来获取底层类型。 这被定义为

template<class T> struct voider { typedef void type; };
template<class T, class = void>
struct data_type {
typedef T type;
};
template<class T>
struct data_type<T, typename voider<typename T::value_type>::type>
: data_type<typename T::value_type> {}; 

使用它我们可以将主Sum修改为

template <class T, class Ret = typename data_type<std::vector<T>>::type>
Ret Sum (const std::vector<T>& v)
{
Ret sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}

所以你可以使用类似的东西

int main()
{
std::cout << Sum(std::vector<std::vector<std::vector<int>>>{{{1},{2},{3}},{{4},{5},{6}}});
}

哪些输出

21

现场示例

你几乎已经自己找到了答案。注意这一行:

sum += Sum(x);

sum的类型,也就是我们所追求的,必须是与我们对Sum递归调用的结果兼容的赋值。根据您的要求,其中一种类型肯定是调用的结果类型。

不过,我们不必仅仅依靠模糊的感觉。元编程毕竟是编程。你可能没有意识到,但你的问题是有根据的递归问题之一,这意味着归纳原理可以引导我们找到答案。

  • 在基本情况下,我们有一个数字,非向量element_type element;,这意味着我们的结果类型是...element_type.实际上,您已经管理了此步骤,这是第一个重载:

    template<typename T>
    T Sum(T element);
    
  • 在递归情况下,我们有:

    • std::vector<element_type> vec;
    • 归纳假设,即:

      // given
      element_type element;
      // we know the following is well-formed and a numerical type
      using recursive_result_type = decltype( Sum(element) );
      

      由于向量元素具有类型element_type,归纳假设为我们提供了对它们调用Sum的结果具有我们想要的所有性质。(我们+=直觉的理由植根于此。我们有我们的安瑟:我们按原样使用recursive_result_type

现在事实证明,第二次重载不能只是这样写,例如:

// doesn't behave as expected
template<typename Element>
auto Sum(std::vector<Element> const& vec) -> decltype( Sum(vec.front()) );

原因是声明的当前Sum重载不在返回类型的作用域中(即使它在定义正文中)。解决此问题的一种方法是依赖类范围,这更适应:

// defining a functor type with operator() overloads
// would work just as well
struct SumImpl {
template<typename Element>
static T apply(Element element)
{ return element; }
template<typename Element>
static auto apply(std::vector<Element> const& vec)
-> decltype( apply(vec.front()) )
{
using result_type = decltype( apply(vec.front()) );
result_type sum = 0;
for(auto const& element: vec) {
sum += apply(element);
}
return sum;
}
};
template<typename Arg>
using sum_result_t = decltype( SumImpl::apply(std::declval<Arg const&>()) );
template<typename Arg>
sum_result_t<Arg> Sum(Arg const& arg)
{ return SumImpl::apply(arg); }

科里鲁演示