简体   繁体   English

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

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

The idea is simple and straight-forward: 这个想法很简单直接:
Keep breaking the n dimensional vector into n-1 dimensional constituent vectors, until you have access to the primitive-datatype objects. 继续将n维向量分解为n-1维构成向量,直到您可以访问primitive-datatype对象。 Then add them all. 然后将它们全部添加。

The problem is, how to infer the return type? 问题是,如何推断返回类型?

It can be done like this, but it already assumes the datatype of summing variable (return-type): 它可以像这样完成,但它已经假定了求和变量的数据类型(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;
}

But I don't want to do it like above. 但我不想像上面那样去做。 I feel like its against the spirit of meta-programming. 我觉得它违背了元编程的精神。

We must infer the return-type by keeping dissecting the vector into its constituent vectors until we reach the primitive-datatype objects, and then choose the return-type as the primitive-datatype. 我们必须通过将向量解析为其构成向量来推断返回类型,直到我们到达primitive-datatype对象,然后选择return-type作为primitive-datatype。

Is it possible in C++ ? 在C ++中有可能吗? (I'm noob in meta-programming) (我是元编程中的noob)


PS PS
std::accumulate() from <numeric> could have been helpful, but it by-passes the problem by inferring the return-type from its third argument __init . 来自<numeric> std::accumulate()可能会有所帮助,但它通过从第三个参数__init推断返回类型来绕过问题。

What we can do is use TC 's data_type class to get the the underlying type. 我们能做的是使用TCdata_type来获取底层类型。 That is defined as 这被定义为

 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> {}; 

Using that we can modify the primary Sum to 使用它我们可以修改主要的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;
}

So then you can use something like 那么你可以使用类似的东西

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

which outputs 哪个输出

21

Live Example 实例

This can be done without any template meta programming. 这可以在没有任何模板元编程的情况下完成。 You can let the compiler infer the type using auto and decltype : 您可以让编译器使用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;
}

The return type of Sum is automatically deduced from sum and the type of sum is whatever Sum(v[0]) returns. 的返回类型Sum自动从推导出sum的类型和sum是任何Sum(v[0])的回报。 Eventually you will end up with the first version of Sum which returns T and the compiler knows that type. 最终,您将得到Sum的第一个版本,它返回T并且编译器知道该类型。

Demo 演示

You've almost figured out the answer for yourself. 你几乎已经找到了自己的答案。 Pay attention to this line: 注意这一行:

sum += Sum(x);

The type of sum , which is what we're after, must be something compatible for assignment with the result of our recursive call to Sum . sum的类型,即我们所追求的,必须与赋值相符,并且我们对Sum的递归调用的结果。 One such type, given your requirements, is certainly the result type of the call. 根据您的要求,其中一种类型肯定是呼叫的结果类型。

We don't have to rely on just a fuzzy feeling though. 我们不必仅依赖于模糊的感觉。 Meta-programming is, after all, programming. 毕竟,元编程就是编程。 You may not have realised it, but your problem is one of well-founded recursion which means that the principle of induction can guide us towards an answer. 您可能没有意识到这一点,但您的问题是有根据的递归之一,这意味着归纳原则可以指导我们找到答案。

  • in the base case, we have a numerical, non-vector element_type element; 在基本情况下,我们有一个数字的非向量element_type element; , meaning our result type is… element_type . ,意思是我们的结果类型是... element_type you've in fact already managed this step, it's the first overload: 你实际上已经完成了这一步,这是第一次重载:

     template<typename T> T Sum(T element); 
  • in the recursive case we have: 在递归的情况下,我们有:

    • std::vector<element_type> vec;
    • the induction hypothesis, ie: 归纳假设,即:

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

      Since the vector elements have type element_type , the induction hypothesis gives us that the result of calling Sum on them has all the properties we want. 由于向量元素具有类型element_type ,因此归纳假设给出了在它们上调用Sum的结果具有我们想要的所有属性。 (The justification for our += intuition is rooted here.) We have our anser: we use recursive_result_type as-is. (我们+=直觉的理由来源于此。)我们有我们的anser:我们使用recursive_result_type as-is。

Now as it turns out that second overload cannot just be written eg like so: 现在,事实证明第二次重载不能只写成如下:

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

The reason being that the current Sum overload being declared is not in scope in the return type (even though it is in the definition body). 原因是声明的当前Sum重载不在返回类型的范围内(即使它在定义主体中)。 One way to work around that is to rely on class scope, which is more accommodating: 解决这个问题的一种方法是依赖类范围,这更适应:

// 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 Coliru演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM