Dimitri Podborski
Dimitri Podborski

Reputation: 3784

Find maximum container size C++

I'm trying to find a good solution for the following problem:

I want to implement a function that takes a variable number of container arguments and returns the size of the biggest container. Here is an example:

std::vector<std::string> vStr(2, "foo");
std::vector<int> vInt(1, 123);
std::vector<double> vDouble(3, 1.1);
std::list<char> lChar(4, '*');
// or even more container

size_t uiMaxSize = getMaxContainerSize(vStr, vInt, vDouble, lChar /*, ...*/);

in this case getMaxContainerSize should return 4, because lChar has the biggest size of 4.

I've already implemented this workaround using cstdarg:

#include <cstdarg>
...
size_t getMaxContainerSize(int iCnt, ... )
{
  size_t uiMaxSize = 0;
  va_list ap;
  va_start(ap, iCnt);
  for(int i=0; i<iCnt; i++)
  {
    size_t uiTempSize = va_arg(ap, size_t);
    uiMaxSize = uiMaxSize<uiTempSize ? uiTempSize : uiMaxSize;
  }
  va_end(ap);
  return uiMaxSize;
}
...
size_t uiMaxSize = getMaxContainerSize( 4, vStr.size(), vInt.size(), vDouble.size(), lChar.size());

But with this I have to type .size() for every container and I also have to specify the number of containers. I also don't like to use C stuff in C++ programs and I'm asking myself if there is a better way to implement this. Maybe by using some class and overloading operator<<() so I can type something like this:

MaxSizeFinder cFinder;
cFinder << vStr << vInt << vDouble << lChar;
size_t uiMaxSize = cFinder.getResult();

Do you think something like this is possible? Any suggestions?

Thank you.

Upvotes: 1

Views: 801

Answers (1)

Qaz
Qaz

Reputation: 61970

Use a variadic template:

template<typename... Conts>
std::ptrdiff_t getMaxContainerSize(const Conts&... conts) {
    return std::max({conts.size()...});
}

When you pass containers as arguments, the compiler will deduce a list of types for Conts. Each parameter of the function will be a const <deduced type> &*. Using conts.size()... expands to conts1.size(), conts2.size(), ..., contsN.size(), where conts# is each argument given to the function. It turns out std::max has a handy overload that you can delegate this to.

There are a couple key advantages of variadic templates over C variadic functions:

  • They are type safe - the compiler is guaranteed to complain when types don't match, and you don't need a format string or anything.
  • The function knows how many arguments were passed, and you can get it with sizeof...(Conts).
  • Nothing special happens to the arguments when going in. In a variadic function, char would be an int by the time the function has to pick it out, among others.
  • You don't need to explicitly specify any of the types when you use the arguments. This means you can accept an infinite number of types instead of a predefined list (think printf's format specifiers).

Finally, per the comments, the return type was changed to a signed type that mostly acts as the signed counterpart to size_t (sort of like the non-standard ssize_t).


To future-proof the answer, there will soon be a std::size for a more generic way to get a container's size:

using std::size;
return std::max({size(conts)...});

This expands similar to above: size(conts1), size(conts2), ..., size(contsN)


*Normally, parameter packs are used with T&&... with std::forward instead of const T&.... This would potentially buy you something with third-party classes that have a more efficient size function when the object used is an rvalue. However, it adds complexity in general for a low chance at any benefit.

Upvotes: 7

Related Questions