Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a function that accepts any container type?

Tags:

c++

c++11

I want to implement a function that takes std::vector or std::array as an argument. How can the parameter list abstract from the container type?

See this example:

// how to implement this?
bool checkUniformity(container_type container)
{
    for(size_t i = 1; i < container.size(); i++)
    {
        const auto& o1 = container[i-1];
        const auto& o2 = container[i];

        if(!o1.isUniform(o2))
            return false;
    }

    return true;
}

struct Foo
{
    bool isUniform(const Foo& other);
}

// I want to call it in both ways:
std::vector<Foo> vec;
std::array<Foo> arr;

bool b1 = checkUniformity(vec);
bool b2 = checkUniformity(arr);

What is the best and most readable way to do this?

Any suggestions for code improvement (style, design) are welcome as well. Thanks!

like image 634
Sparkofska Avatar asked Mar 18 '26 14:03

Sparkofska


2 Answers

If you use iterators and ranges instead of working with the container directly, you can produce an algorithm that works with any container (including linked lists) efficiently and also with streams:

#include <list>
#include <array>
#include <vector>
#include <iterator>

template <typename T>
bool checkUniformity(T begin, T end) {
    // Check for empty range
    if (begin == end) return true;

    // Remember last element
    T last = begin;
    while (++begin != end) {
        if (!((*last).isUniform(*begin)))
            return false;
        last = begin;
    }

    return true;
}

template <typename T, typename F>
bool checkUniformity(T begin, T end, F pred) {
    // Check for empty range
    if (begin == end) return true;

    // Remember last element
    T last = begin;
    while (++begin != end) {
        if (!pred(*last, *begin))
            return false;
        last = begin;
    }

    return true;
}

struct Foo
{
    bool isUniform(const Foo& other) const;
};


int main () {
    // I want to call it in both ways:
    std::vector<Foo> vec;
    std::array<Foo, 3> arr;
    std::list<Foo> list;
    Foo carr [3];

    bool b1 = checkUniformity(std::cbegin(vec), std::cend(vec));
    bool b2 = checkUniformity(std::cbegin(arr), std::cend(arr));
    bool b3 = checkUniformity(std::cbegin(list), std::cend(list));
    bool b4 = checkUniformity(std::cbegin(carr), std::cend(carr));

    bool b1_2 = checkUniformity(std::cbegin(vec), std::cend(vec), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
    bool b2_2 = checkUniformity(std::cbegin(arr), std::cend(arr), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
    bool b3_2 = checkUniformity(std::cbegin(list), std::cend(list), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
    bool b4_2 = checkUniformity(std::cbegin(carr), std::cend(carr), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
}

You can also implement a second variant as shown, where you can specify the condition as a predicate (e.g. a lambda, as shown) in case you have different variants of isUniform. Having to pass two parameters for the range instead of just the container is slightly more cumbersome, but much more flexible; it also allows you to run the algorithm on a sub-range of the container.

This is the same approach as used by standard-library algorithms, such as std::find.

like image 196
Erlkoenig Avatar answered Mar 21 '26 03:03

Erlkoenig


You want template:

template <typename container_type>
bool checkUniformity(const container_type& container)
{
    for(size_t i = 1; i < container.size(); i++)
    {
        const auto& o1 = container[i-1];
        const auto& o2 = container[i];

        if(!o1.isUniform(o2))
            return false;
    }
    return true;
}
like image 32
Jarod42 Avatar answered Mar 21 '26 03:03

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!