Template function with macro - accumulate on vecto

2019-05-20 00:32发布

问题:

I want to create a function that get vector<int> run over all his elements and "sum" them according to specific operator I chose .

For example , v1 = [3,6,7] so I could calculate by this function - 3+6+7 of 3-6-7 of 3*6*7 etc ..

For this I did -

#include <iostream>
#include <vector>

using namespace std;

#define     OPERATOR(X,Y,OP)  X #OP Y

template<T>
int allVectorWithOperator(vector<int> &myVector, T) {
    vector<int>::iterator it;
    vector<int>::iterator oneBeforeFinal;
    oneBeforeFinal = myVector.end();
    oneBeforeFinal -= 2;
    int sum = 0;
    for (it = myVector.begin(); it <= oneBeforeFinal; it++) {
        sum = OPERATOR(*(it),*(it+1),T);
    }
    return sum;

}

int main() {
    vector<int> myVector;
    myVector.push_back(3);
    myVector.push_back(6);
    myVector.push_back(7);
cout << "run over all the vector with * is :" << allVectorWithOperator(myVector,*)<<endl;
// here I want to get 3*6*7    

}

I don't control very well in such cases of template so as you can see this code doesn't work, but I think you understand what is my goal. How can I fix it to work fine?

Edit:

according the 2 answer I got I changed the code section to -

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

template<typename T>
int allVectorWhitOperator(vector<int> &myVector, const T& func) {
    int sum = std::accumulate(myVector.begin(), myVector.end(), 1, func);
    return sum;

}

int main() {
    vector<int> myVector;
    myVector.push_back(3);
    myVector.push_back(4);
    myVector.push_back(6);
    cout << "accumulate the vector with * is :"
            << allVectorWhitOperator(myVector, std::multiplies<int>()) << endl;

}

And it work fine ! indeed I got accumulate the vector with * is :72

回答1:

This is basically just std::accumulate. Assuming the vector is not empty, you could rewrite the function as:

template <typename C, typename F>
typename C::value_type fold(const C& container, const F& function) {
    typename C::iterator cur = container.begin();
    typename C::value_type init = *cur++;
    return std::accumulate(cur, container.end(), init, function);
}

...

int sum = fold(myVector, std::plus<int>());
int difference = fold(myVector, std::minus<int>());
int product = fold(myVector, std::multiplies<int>());

Now, about your implementation:

  1. As shown in the example above, to declare a type parameter in the template, you need to have the typename or class keyword: template <typename T> int allVectorWithOperator( ... )

  2. A lone * won't be a valid syntax. But C++ provides a lot of "function objects" which serve wraps these operators so that you could use them with the function notation. For example,

    std::multiplies<int> f; // f is a now function that multiplies 2 numbers
    int product = f(5, 7);  // p == 35;
    

    so you could write:

    template<typename T>
    int allVectorWithOperator(vector<int> &myVector, T func) {
        ....
        for (it = myVector.begin(); it != oneBeforeFinal; ++ it) {
            sum = func(*it, *(it+1));
        }
    }
    

    Also, some minor points: (1) Usually we compare iterators with != instead of <=, because many iterators don't support the <= operator, (2) ++it is more efficient than it++ in general.

  3. Macros and templates are processed in different stages. In particular, you can't pass a template or function parameter to a macro, because all macros are already evaluated when templates are considered. To achieve your syntax, the whole allVectorWithOperator must be written as a macro, e.g. (assuming C++11 can be used):

    #define allVectorWithOperator(container, binaryOp) \
        ([&]() -> std::remove_reference<decltype(*(container).begin())>::type { \
            auto&& c = (container); \
            auto cur = c.begin(); \
            auto val = *cur++; \
            auto end = c.end(); \
            while (cur != end) { val binaryOp##= *cur++; } \
            return val; \
        }())
    

    Yes it's a complete mess, so you should prefer not to use macros if possible. BTW, #OP means turning the OP into a string. You don't really need the #.



回答2:

The standard library already has the operations in <algorithm><numeric>.

You can use

int sum = std::accumulate(MyVector.begin(), MyVector.end(), 0);

to add up all the elements.

If you want to compute the product (instead of using the default operator+), you can pass an additional parameter

int product = std::accumulate(MyVector.begin(), MyVector.end(), 1,
                              std::multiplies<int>());