【问题标题】:How to sum all the elements of a multi-dimensional std::vector?如何对多维 std::vector 的所有元素求和?
【发布时间】:2017-10-16 21:25:18
【问题描述】:

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

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

可以这样做,但是已经假设了求和变量的数据类型(return-type):

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++ 中可以吗? (我是元编程的菜鸟)


P.S.
&lt;numeric&gt; 中的std::accumulate() 可能会有所帮助,但它通过从第三个参数 __init 推断返回类型来绕过问题。

【问题讨论】:

  • SumType 在编译时必须是可推断的,就像模板一样,编译器知道要生成哪些函数原型。您可以查看decltype 以从基础数据中定义 SumType 的返回值。要获得更灵活的解决方案,请考虑将基础数据包装到变体类中,例如脚本语言。
  • 你可能想要SumType sum {}。没有明显的理由假设数字 0 可以转换为 SumType

标签: c++ multidimensional-array metaprogramming stdvector


【解决方案1】:

这可以在没有任何模板元编程的情况下完成。您可以让编译器使用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]) 返回的任何类型。最终你会得到Sum 的第一个版本,它返回T 并且编译器知道那个类型。

Demo

【讨论】:

    【解决方案2】:

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

    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
    

    Live Example

    【讨论】:

      【解决方案3】:

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

      sum += Sum(x);
      

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

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

      • 在基本情况下,我们有一个数字的、非向量的element_type element;,这意味着我们的结果类型是……element_type。你实际上已经完成了这一步,这是第一个重载:

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

        • std::vector&lt;element_type&gt; 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); }
      

      Coliru demo

      【讨论】:

        猜你喜欢
        • 2011-08-22
        • 2011-12-27
        • 2014-01-29
        • 1970-01-01
        • 2017-12-15
        • 2011-03-18
        • 1970-01-01
        • 2021-08-21
        • 1970-01-01
        相关资源
        最近更新 更多