Best way to test for equality of two valarray

2019-07-11 03:09发布

问题:

I think that the default overload of == for valarray is not very convenient. By default x==y (for two valarrays x and y) returns a valarray<bool>, with true on the ith entry if x[i]==y[i]. Rather, I need a single bool, which tells me if both valarray<double> contain the same elements or not. I know I can do this with a cycle, but having to do the cycle every time is not convenient. What's the best workaround here? Is there a way for me to define my own overload of == (and also !=, <, and so on)?

回答1:

"Not very convenient"? This behaviour is exactly the reason for valarray.

To override it would be entirely self-defeating.

If you don't like it, just use a vector instead.



回答2:

Do not override the default operator== use for example this instead:

bool isEqual( const std::valarray< bool >& aResult )
{
    bool equals = true;
    for ( auto item : aResult )
    {
        equals &= item;
    }
    return equals;
}

And then use it:

std::valarray< int > x;
std::valarray< int > y;

bool equals = isEqual( x == y );


回答3:

I agree with the others not to override the == operator. The reason is that those operators are the major reason to use a valarray. If you do not need element-wise operators simply do not use valarray. Also you might need the original version of the operator at some point so why would you throw it away?

I like p.i.g s solution, but if efficiency is a major concern, I would modify it like this:

#include <iostream>
#include <valarray>

template <typename T>
bool isEqual( const std::valarray<T>& x,const std::valarray<T>& b) {
    using Iter = typename std::valarray<T>::const_iterator;
    using IterP = std::pair<Iter,Iter>;
    bool equals = true;

    for (IterP it(std::begin(x), std::begin(b));it.first != std::end(x);++it.first,++it.second) {
        equals &= ((*it.first) == (*it.second));
        if (!equals) break;
    }
    return equals;
}

and use it like this

valarray<T> x,y;
bool b = isEqual(x,y);

By not using the built-in == not all elements in the valarrays have to be compared.

PS:

+ was just an example. I also want -, *, and so on. Moreover, I know that valarray has contrived versions of these operators which are more efficient than a naive implementation (I think they use proxy classes to store intermediate results in expressions like x + y + z and then evaluate the whole expression together). I would like to take advantage of those.

Actually, thats interesting and I didnt know about this before. And the conclusion should be: Do not override those operators, otherwise you cannot take advantage of their clever implementations.



回答4:

If you are decided on "overriding" std::valarray's operator== for commodity (instead of writing a named function like in user463035818's answer), you could write a custom "adaptor" class:

template<typename T>
class CustomValArray : public std::valarray<T> {
public:
    typedef std::valarray<T> base;
    // We need to re-implement any non-default constructors we want to use:
    CustomValArray(std::initializer_list<T> init) : base(init) {}
};

/// Accumulation (single bool) comparison 
template<typename T>
bool operator==(const CustomValArray<T> &lhs, const CustomValArray<T> &rhs) {
    return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
}

If you also wanted to be able to use the original operator== for std::valarray, you could do so by writing a named function:

/// Element-wise comparison
template<typename T>
auto elementWiseEqual(const CustomValArray<T> &lhs, const CustomValArray<T> &rhs)  {
    // We delegate to operator==(const std::valarray<T> &, const std::valarray<T> &)
    typedef std::valarray<T> base;
    return dynamic_cast<const base &>(lhs) == dynamic_cast<const base &>(rhs);
}

Note: the snippet above uses C++14's automatic return type deduction. If you're on C++11, you should add something like -> decltype(std::valarray<T>() == std::valarray<T>()) to the end of the declarator.

Try it here.

If you choose to do it like this, inheriting directly from std::valarray, be aware of the possible risks of inheriting from an STL class.


As an alternative, you could have a wrapper class with a std::valarray<T> private member, and manually delegate any member functions you want to use to std::valarray.



标签: c++ valarray