如何总结多维std :: vector的所有元素?

时间:2017-05-17 12:08:52

标签: c++ multidimensional-array metaprogramming stdvector

这个想法很简单直接:
继续将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;
}

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

我们必须通过将向量解析为其构成向量来推断返回类型,直到我们到达primitive-datatype对象,然后选择return-type作为primitive-datatype。

在C ++中有可能吗? (我是元编程中的noob)

P.S。
来自std::accumulate()的{​​{1}}可能会有所帮助,但它会通过从第三个参数<numeric>推断返回类型来绕过问题。

3 个答案:

答案 0 :(得分:2)

我们可以做的是使用T.C.&#39; 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

答案 1 :(得分:2)

这可以在没有任何模板元编程的情况下完成。您可以让编译器使用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 :(得分:1)

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

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的结果具有我们想要的所有属性。 (我们的+=直觉的理由植根于此。)我们有我们的anser:我们按原样使用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