Former article was visited by C++ fans from around the world. Therefore i decided to publish my article in english. Thanks for visiting web site.
More Generic Design
You know templates provide more generic design than function overloading. And now with C++11, you can even design more generic templates than templates, so variadic templates.
I want to write a function that gives me size of all containers in STL(even if they almost all have a size function 🙂 ). You know STL has two main container types:
– Sequence Containers
– Associative Containers
Sequence containers take 2 arguments ValueType and mainly Allocator:
template<class T, class Allocator = std::allocator<T> > class SeqContainer;
I will try to implement my function-template for sequence containers.
template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc> size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c) { size_t s = 0; for (auto element : c) { s++; } return s; }
As you see first template parameter “template class ContainerType” is a template class as template parameter. Second and third one are template parameters of first parameter.
But that function template will not work for std::array type. We will look that problem later. Mainly this function satisfies our needs.
#include <vector> #include <iostream> #include <memory> #include <map> #include <forward_list> #include <unordered_set> #include <array> using namespace std; template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc> size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } int main() { vector<int> v({1, 4, 5, 3, 5, 6}); forward_list<int> fl ({ 34, 99, 10, 71}); cout << sizeOfContainers(v) << endl; cout << sizeOfContainers(fl) << endl; return 0; }
Associative containers take 3 or 4 arguments for example:
std::set
template< class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key> > class set;
std::map
template< class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > class map;
std::unordered_set
template< class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>, class Allocator = std::allocator<Key> > class unordered_set;
Therefore we need two more overload for our function to handle this containers. Let’s try to write them :
template < template <typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename AllocType> size_t sizeOfContainers(ContainerType<FirstType, SecondType, AllocType> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } template < template <typename, typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename ThirdType, typename AllocType> size_t sizeOfContainers(ContainerType<FirstType, SecondType, ThirdType, AllocType> &c) { size_t s = 0; for (auto element : c) { s++; } return s; }
As you see for that functions can handle this issue for different type.
But now we have three overloads, can we reduce it to one function with variadic functions ? Lets try it:
template <template <typename, typename...> class ContainerType, typename FirstType, typename... Types> size_t sizeOfContainers(ContainerType<FirstType, Types...> &c) { size_t s = 0; for (auto element : c) { s++; } return s; }
Now we can handle all 2,3,4 parameter containers. Except one std::array. std::array takes two parameter:
template< class T, std::size_t N > struct array;
Remember, we discussed variadic template function creation at first article. In that example our variadic function will not create function that we need for std::array. Actually second type of std::array is not a type, it is a constant. And it is not allowed in C++, also compile time error. Function overloading take responsibility for us :
template <template <typename, size_t> class ContainerType, typename FirstType, size_t S> size_t sizeOfContainers(ContainerType<FirstType, S> &c) { size_t s = 0; for (auto element : c) { s++; } return s; }
That overload will handle our issue. Sometimes we need partial specialization overloads to resolve problems.
At the end of the day score is on the board:
Variadic templates 3 – 1 Function overloading
Full Source Code:
#include <vector> #include <cstdlib> #include <iostream> #include <memory> #include <map> #include <forward_list> #include <unordered_set> #include <array> #include <set> #include <deque> using namespace std; template <template <typename, typename...> class ContainerType, typename FirstType, typename... Types> size_t sizeOfContainers(ContainerType<FirstType, Types...> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } template <template <typename, size_t> class ContainerType, typename FirstType, size_t S> size_t sizeOfContainers(ContainerType<FirstType, S> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc> size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } template < template <typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename Alloc> size_t sizeOfContainers(ContainerType<FirstType, SecondType, Alloc> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } template < template <typename, typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename ThirdType, typename Alloc> size_t sizeOfContainers(ContainerType<FirstType, SecondType, ThirdType, Alloc> &c) { size_t s = 0; for (auto element : c) { s++; } return s; } int main() { vector<int> v({1,2,3,4,5,6}); map<int, int> m({ {2,3}, {4, 5}}); set<int> s({8, 7}); deque<int> d = {10,20,30}; forward_list<int> fl ({ 34, 77, 16, 2 }); array<int,10> a = { 2, 16, 77, 34, 50}; unordered_set<string> us = {"C++", "UG","ISTANBUL","69736c616d"}; cout << sizeOfContainers(v) << endl; cout << sizeOfContainers(m) << endl; cout << sizeOfContainers(fl) << endl; cout << sizeOfContainers(us) << endl; cout << sizeOfContainers(s) << endl; cout << sizeOfContainers(a) << endl; cout << sizeOfContainers(d) << endl; return 0; }