C++11- Makrolardan değişken parametreli şablon fonksiyonlarına(variadic templates) bir yolculuk - 2


#1

Daha Kapsamlı Tasarım

Bildiğiniz gibi template’ler function overloading’ten daha kapsamlı (generic) bir tasarım sunmaya olanak sağlıyor. Kısacası birden fazla function overloading yaparak elde ettiğiniz işlevi, tek bir function template ile elde edebilirsiniz. C++11 ile templatelerden daha kapsamlı bir tasarım olanağına sahipsiniz: variadic templates.

Örnek olması hasebiyle STL veri yapılarının büyüklüğünü(size) ölçen bir fonksiyon yazalım, her ne kadar her biri böyle bir fonksiyona sahip olsa da.

Bildiğiniz gibi STL temel manada iki farklı container tipi vardır:

  • Sequence Containers
  • Associative Containers

Sequence container’lar 2 adet şablon parametresi alır, biri tutulacak değerin tipi diğeri ise memory işlemleri için Allocator. Allocator için zaten default olarak bir değer vardır, istenirse değiştirilebilir:

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

Şimdi sequence container’lar için başlangıçta düşündüğümüz fonksiyonu yazarsak;

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

Birinci parametre görüldüğü üzere class template, diğerleri ise sırasıyla valutype ve allocator. Bu yazdığımız fonksiyon genel manada ihtiyaçlarımızı karşılayacak, std::array dışında…

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

Assosicative containerlar ise 3 yada 4 parametre alan şablon sınıflarıdır;

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;

Dolayısıyla bu fonksiyonumuz bu containerlar için işlevsiz hale geliyor. Bu containerlar için de bir overload yazmamız lazı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;
}

Bu overloadlarla beraber 3 tane şablon fonksiyonumuz oldu. Bu da kod fazlalığı zaten anlaşılması, okuması zor olan template fonksiyonlarını iyice içinden çıkılmaz hale sokuyor. Peki bu fonksiyonları variadic templates kullanarak, tek bir fonksiyon haline getirebilir 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;
}

Evet bu fonksiyonla artık std::array hariç diğer containerların büyüklüğünü ölçebiliyoruz.

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

Görüldüğü üzere std::array’in ikinci parametresi sabit(constant) bir değer istiyor. Yani bir tür değil dolayısıyla std::array için function overloadingten başka çaremiz yok;

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

Evet günün sonunda Variadic templates 3 - 1 Function Overloading… C++11-14-17 ile maceramız devam edecek…

Kaynak Kodun Tamamı:

#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++", "HerbiBK","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;
}

C++11 - std::thread