C++11 - Macro’s vs Variadic Templates Part 2

Daha Soyut Tasarım

Bildiğiniz gibi şablon sınıfları fonksiyon yüklemelerine göre daha soyut bir
tasarım sağlarlar. Şimdi C++11 ile şablonlardan daha soyut tasarım imkanı
sağlayan bir yapı var; değişken parametreli şablonlar.

Basit bir şekilde variadic templateleri (değişken parametreli şablon sınıfları)
kullanarak her ne kadar hepsinin bir size fonksiyonu olsa da STL’deki tüm
konteynırlara (container) bir size() fonksiyonu yazalım.

Bildiğiniz gibi STL’deki temel olarak iki konteynır çeşidi var :

  • Sıralı Konteynırlar (Sequence Containers)
  • İlişkili Konteynırlar (Associative Containers)

Sıralı konteynırlar2 parametre alırlar değer tiği (Value Type) ve
allocator (bellek yöneticisi):

template<class T,
class Allocator = std::allocator<T>
> class SeqContainer;

Şimdi sıralı konteynırlar için size() fonksiyonunu kodlamaya çalışalım.

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;
}

Görüldüğü gibi şablon sınıfımızın ilk parametresi kendisi de bir şablon sınıfı
olan konteynır türümüz. İkinci ve üçüncü parametreler ise klasik sıralı konteynır
parametreleri.

Fakat bu şablon sınıfı std::array için çalışmayacaktır bu soruna daha sonra döneceğiz.
Bu hali dahi genel ihtiyaçlarımızı karşılayacak nitelikte.

#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;
}

İlişkili Konteynırlar ise genelde 3 veya 4 parametre alırlar. Örneğin;

template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;

template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;

template<
class Key,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>
> class unordered_set;

Bu yüzden yazacağımız şablon sınıfı da bu değişkenliği hesaba katmak zorunda.
Hadi şimdi yazmaya çalışalım.

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;
}

Görüldüğü üzere yazdığımız şablon sınıfları fonksiyon yüklemesiyle bu sorunu çözüyor. Peki tek bir sınıfa indirgeyebilir miyiz ?

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;
}

İşte şimdi 2, 3, 4 parametreli değişkenleri ele alabiliyoruz. İki parametreli std::array bu hesaba dahil değil şimdi ona bakalım.

template<
class T,
std::size_t N
> struct array;

Hatırlarsanız ilk paragraflarda variadic şablon sınıfımız bizim için gerekli olan size() fonksiyonunu oluşturmamıştı. Aslında bunun
sebebi std::array şablon sınıfının ikinci parametresi bir sabit (constant). Bu durumda fonksiyon yüklemelerine (function overloading)
başvurmak akıllıca bir çözüm olacaktır.

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;
}

Overload(yükleme) bizim için sorunu çözdü. Aşağıya tüm kaynak kodu ve test kodu ekliyorum.

#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;
}