Home > Enterprise >  How to write a template or set of template functions for checking all given containers have the same
How to write a template or set of template functions for checking all given containers have the same

Time:10-29

I would like to write a variadic template function that takes an arbitrary number of containers of any type (though realistically I will only use it for vectors I would say), and return true if they all have the same size (so .size() method or std::size (same thing)). I tried to implement this using my own understanding of template programming but I failed. I tried to use a set of recursive functions to this end but without luck, it's clear to me that the wrong functions are being chosen by the compiler.

My implementation attempt, with reasoning

This is the function that I would like to be called first, and is the entry point. I have two arguments, one normal template argument and another as a parameter pack. This is so I can separate the "head" from the "tail" (which I am used to with car and cdr from lisp, maybe this is the wrong paradigm here though).

template <typename Arg, typename... Args>
bool all_equal_in_size(Arg arg, Args... args) {
    auto size = arg.size();
    return (size == all_equal_in_size(size, args...));
}

This should be the function called recursively, same as above but with the state (size) provided additionally in the parameter list. This is also familiar to me from functional programming, as a means to propagate state through a recursive call chain.

template <typename Arg, typename... Args>
bool all_equal_in_size(std::size_t n, Arg arg, Args... args) {
    return ((n == arg.size()) && all_equal_in_size(args...));
}

This is simply the terminal condition, and ends the recursive call chain.

template <typename T> bool all_equal_in_size(unsigned long n, T ele) {
    return (n == ele.size());
}

The Problem

So in essence I would like to use it like this (sorry if ctor args are wrong, point is I want some containers with length 5):

std::vector<int> v1(5);
std::vector<int> v2(5);
std::vector<float> v3(5);
std::vector<double> v4(5);
std::list<int> v5(5);

all_of_equal_size(v1, v2, v3, v4, v5); // == true

But when I try to do this I find that the compiler complains because the wrong functions are found:

bunch.hpp:13:24: error: member reference base type 'unsigned long' is not a structure or union
    auto size = arg.size();

It's trying to call size on a long, which I don't want. If I reorder the template definitions then I get even more scary compiler warnings. So where am I going wrong? Is there a simpler way to do this? Why is it picking the wrong template?

Thank you for reading.

CodePudding user response:

With template and overload, you have to be careful with order (if you don't provide forward declaration).

template <typename Arg, typename... Args>
bool all_equal_in_size(Arg arg, Args... args) {
    auto size = arg.size();
    return (size == all_equal_in_size(size, args...)); // only see itself, and not other won't be found by ADL
}

in addition, you miss one case, in:

template <typename Arg, typename... Args>
bool all_equal_in_size(Arg arg, Args... args)
{
    auto size = arg.size();
    return (size == all_equal_in_size(size, args...));
}

With empty pack, you call all_equal_in_size(size) which can match only itself, adding

bool all_equal_in_size(std::size_t n) { return true; }

solves that.

So you have to order definition in right order (or providing forward declaration of the methods) and add missing overload:

Demo

You can drop recursion with the unique function:

template <typename Arg, typename... Args>
bool all_equal_in_size(Arg arg, Args... args) {
    std::size_t sizes[] = {arg.size(), args.size()...};
    return std::all_of(sizes,
                       sizes   1   sizeof...(Args),
                       [&](std::size_t size){ return size == std::size(arg); });
}

Demo or in C 17

template <typename Arg, typename... Args>
bool all_equal_in_size(Arg arg, Args... args) {
    return ((std::size(arg) == std::size(args)) && ... && true);
}

Demo

CodePudding user response:

With C 17 you can use fold expressions:

template <typename Arg, typename... Args>
bool all_equal_in_size(Arg const& head, Args const&... tail){
    return  ( (tail.size() == head.size()) && ... );
}

Demo

  • Related