How to compare and assign between std::vector<T> and std::vector<std::reference_wrapper<T>>?

218 Views Asked by At

Here are two different type of std::vector, as an example:

std::vector<std::reference_wrapper<MyClass>> rv;
std::vector<MyClass> v;

A possible way to assign between them is:

for (const auto& mc : rv) {
    v.push_back(mc.get());
}

It works. But ugly and maybe slow. The same as comparison:

bool is_same(std::vector<MyClass>& v, std::vector<std::reference_wrapper<MyClass>>& rv) {
    if (v.size()!=rv.size()) {
        return false;
    }
    for (size_t i = 0; i < v.size(); v++) {
        if (v[i]!=rv[i].get()) {
            return false;
        }
    }
    return true;
}

Is there any better way to do this work? Smart and quick.

2

There are 2 best solutions below

2
On

Since std::reference_wrapper is implicitly convertible to a reference to the type it holds, you can assign one to MyClass. So a better way to initialize one with the other is the appropriate vector constructor:

std::vector<MyClass> v(begin(rv), end(rv));

Or, if you really need to assign:

v.assign(begin(rv), end(rv));

You can do the comparison by applying the std::mismatch algorithm, again thanks to the implicit conversion provided by std::reference_wrapper:

bool is_same(std::vector<MyClass> const& v, std::vector<std::reference_wrapper<MyClass>> const& rv) {
  return v.size() == rv.size() &&
         end(v) == std::mismatch(begin(v), end(v), begin(rv), end(rv)).first;
}

As a general rule of thumb, it's always good to consult the standard algorithm library before writing loops yourself. It makes your own code more readable by giving verbs and nouns to computation steps. And has the benefit of allowing for any optimizations the standard library can offer.


As cppleaner pointed out, I should have consulted the library more closely myself. is_same can be implemented even more easily by a simple call to std::equal

return std::equal(begin(v), end(v), begin(rv), end(rv));
0
On

Idiomatic is_same implementation:

bool is_same(std::vector<MyClass>& v, std::vector<std::reference_wrapper<MyClass>>& rv) {

    return std::equal(begin(v), end(v), begin(rv), end(rv),
        [](const MyClass& c, const std::reference_wrapper<MyClass>& rc) {

        return c == rc.get();
    });
}

Alternately, you can write custom:

class MyClass {
    bool operator ==(const MyClass&);
};

bool operator != (const MyClass&, const MyClass&);
bool operator == (const MyClass&, const std::reference_wrapper<MyClass>&);
bool operator == (const std::reference_wrapper<MyClass>&, const MyClass&);
bool operator != (const MyClass&, const std::reference_wrapper<MyClass>&);
bool operator != (const std::reference_wrapper<MyClass>&, const MyClass&);

and client code (this is redundant: you could just use the implementation as an operation):

bool is_same(const std::vector<MyClass>& v,
             const std::vector<std::reference_wrapper<MyClass>>& rv) {
    return v == rv;
}

Note: Consider using const on your arguments, where possible.