5

Let's say I have n arrays (right now I have 4 but that might go up) and they're supposed to be of the same size, but since I'm fetching them from outside the code I want to be sure

std::vector<int> v0 = {1,2,3};
std::vector<Custom> v1 = {Custom("a"), Custom("b"), Custom("c")};
std::vector<double> v2 = {7.,8.,9.};
std::vector<QColor> v3 = {Qt::Black, Qt::White, Qt::Orange};

if(v0.size() == v1.size() && v1.size() == v2.size() && v2.size() == v3.size()) {
    return true;
} else {
    return false;
}

Is there any elegant way to make a function that would check for any mismatch in size ?

3
  • 2
    You can add a struct/class containing data members for all the data (of types int, Custom, double etc.) and then have one std::vector holding this aggregate type. Commented Dec 6 at 15:40
  • Or you can just make a struct with those for types of members and make one vector on that (assuming you're not trying to optimize caching or something). Commented Dec 6 at 16:17
  • 1
    Minor point: if (x) return true; else return false; can be written much more clearly as return x;. Commented Dec 6 at 19:25

4 Answers 4

10

A simple way to do this is with a variadic template and a fold expression. That would give you a function like

template <typename T, typename... Ts>
bool all_same_size(T&& first, Ts&&... rest)
{
    return ((first.size() == rest.size()) && ...);
}

The (first.size() == rest.size()) compares the first container to the second one and then then && ... will logical and that with first and every other member of rest.

Sign up to request clarification or add additional context in comments.

Comments

2

Assuming the arrays are intended to be "parallel" and provide various attributes, of a certain number of things, rather than multiple arrays/vectors have one array/vector with aggregate data.

E.g.

#include <string>
#include <vector>

int main() {
   std::vector<std::string> first_names = {"Bob", "John"};
   std::vector<std::string> sur_names = {"Smith", "Doe"};
} 

Rather:

#include <string>
#include <vector>

struct Name {
    std::string first;
    std::string sur;
};

int main() {
   std::vector<Name> names = {{"Bob", "Smith"}, {"John", "Doe"}};
} 

You also have a very inelegant conditional.

if(v0.size() == v1.size() && v1.size() == v2.size() && v2.size() == v3.size()) {
    return true;
} else {
    return false;
}

Can just be:

return v0.size() == v1.size() && v1.size() == v2.size() && v2.size() == v3.size();

Comments

2

For checking that values A1,A2,...An are all equals, you can check that the two arrays A1,A2,...An and A2,...,An,A1 are equal.

So a variation on NathanOliver' answer (which uses fold expressions) would be (using pack expansions)

template <typename T, typename... Ts>
bool all_same_size (T&& first, Ts&&... rest) {
    return std::array {first.size(), rest.size()...} == std::array {rest.size()..., first.size()};
}

Demo

9 Comments

Nice trick (+1 for the idea), but in practice - what advantage does it have over @NathanOliver's solution which is shorter and clearer (and might also be more efficient as much as it is a concern) ?
@wohlstad, this is more for pointing out the idea about how comparing N values by rotating the array. I guess it might be useful in a context where one needs to keep the array of values for further processing. But in the case of the OP's question, I also prefer NathanOliver's solution :)
@wohlstad, you can also see usage of this kind of trick in another context here
It is indeed a nice trick for the toolbox.
Since this code uses CTAD it requires C++17 and this version of C++ standard also have "fold expressions" which fits better to this problem. The idea is good, but should be tweaked to so it works with C++14 or C++11. C++11 Example.
Improved version with shorter tuple.
|
1

If you need to keep your data consistent then consider a ComponentStorage model like this:
This will guarantee your vectors always have the same size.

/// Example struct with 4 members
struct Data {
    int id;
    Custom name;
    double value;
    QColor color;
};

/// View struct providing references to component data
struct DataView {
    int& id;
    Custom& name;
    double& value;
    QColor& color;
};


class ComponentStorage {
private:
    std::vector<int> m_ids;
    std::vector<Custom> m_names;
    std::vector<double> m_values;
    std::vector<QColor> m_colors;
    
public:
    /// Adds a new data element
    void add(const Data& data) {
        m_ids.push_back(data.id);
        m_names.push_back(data.name);
        m_values.push_back(data.value);
        m_colors.push_back(data.color);
    }
    
    /// Returns a mutable view of a data element at the given index
    [[nodiscard]] DataView get(std::size_t index) {
        if (index >= size()) {
            throw std::out_of_range("Index out of bounds");
        }
        return DataView{
            m_ids[index],
            m_names[index],
            m_values[index],
            m_colors[index]
        };
    }
    
    /// Updates a data element at the given index
    void set(std::size_t index, const Data& data) {
        if (index >= size()) {
            throw std::out_of_range("Index out of bounds");
        }
        m_ids[index] = data.id;
        m_names[index] = data.name;
        m_values[index] = data.value;
        m_colors[index] = data.color;
    }
    
    /// Returns the number of elements
    [[nodiscard]] std::size_t size() const {
        return m_ids.size();
    }
    
    /// Checks if storage is empty
    [[nodiscard]] bool empty() const {
        return m_ids.empty();
    }
    
    /// Returns reference to underlying id vector for direct access
    [[nodiscard]] std::vector<int>& ids() { return m_ids; }
    
    /// Returns reference to underlying name vector for direct access
    [[nodiscard]] std::vector<Custom>& names() { return m_names; }
    
    /// Returns reference to underlying value vector for direct access
    [[nodiscard]] std::vector<double>& values() { return m_values; }
    
    /// Returns reference to underlying color vector for direct access
    [[nodiscard]] std::vector<QColor>& colors() { return m_colors; }
};

// Example usage
ComponentStorage storage;
storage.add(Data{1, Custom("a"), 7.0, Qt::Black});
storage.add(Data{2, Custom("b"), 8.0, Qt::White});
storage.add(Data{3, Custom("c"), 9.0, Qt::Orange});

// Retrieve and check with view (returns references)
if (!storage.empty() && storage.size() == 3) {
    auto view = storage.get(0);  // Returns DataView with references
    return view.id == 1 && view.value == 7.0;
} else {
    return false;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.