Şablonlar (C++)

Şablonlar, C++ dilinde genel programlama için temel oluşturur. Kesin olarak belirlenmiş bir dil olarak, C++ tüm değişkenlerin programcı tarafından açıkça bildirilen veya derleyici tarafından oluşturulan belirli bir türe sahip olmasını gerektirir. Ancak, birçok veri yapısı ve algoritma, hangi tür üzerinde çalışırlarsa olsunlar aynı görünür. Şablonlar, bir sınıfın veya işlevin işlemlerini tanımlamanızı ve kullanıcının bu işlemlerin hangi somut türlerde çalışacağını belirtmesini sağlar.

Şablonları tanımlama ve kullanma

Şablon, kullanıcının şablon parametreleri için sağladığı bağımsız değişkenlere göre derleme zamanında sıradan bir tür veya işlev oluşturan bir yapıdır. Örneğin, aşağıdaki gibi bir işlev şablonu tanımlayabilirsiniz:

template <typename T>
T minimum(const T& lhs, const T& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

Yukarıdaki kod, dönüş değeri ve çağrı parametrelerinin (lhs ve rhs) tümü bu tür olan tek tür parametreli bir genel işlev için bir şablon açıklar. Tür parametresini istediğiniz şekilde adlandırabilirsiniz, ancak kurala göre tek büyük harfler en yaygın olarak kullanılır. T bir şablon parametresidir; typename anahtar sözcüğü bu parametrenin bir tür için yer tutucu olduğunu söyler. İşlev çağrıldığında, derleyici her örneğini T kullanıcı tarafından belirtilen veya derleyici tarafından oluşturulan somut tür bağımsız değişkeniyle değiştirir. Derleyicinin şablondan bir sınıf veya işlev oluşturduğu işlem, şablon örneği oluşturma olarak adlandırılır; minimum<int> şablonun minimum<T>bir örneğidir.

Başka bir yerde, kullanıcı int için özelleştirilmiş bir şablon örneği bildirebilir. get_a() ve get_b() işlevlerinin int döndüren işlevler olduğunu varsayalım:

int a = get_a();
int b = get_b();
int i = minimum<int>(a, b);

Ancak, bu bir işlev şablonu olduğundan ve derleyici a ve b bağımsız değişkenlerinden türünü T çıkarabildiğinden, bunu sıradan bir işlev gibi çağırabilirsiniz:

int i = minimum(a, b);

Derleyici bu son deyimle karşılaştığında, şablondaki her T örneğinin ile intdeğiştirildiği yeni bir işlev oluşturur:

int minimum(const int& lhs, const int& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

Derleyicinin işlev şablonlarında tür kesintisi gerçekleştirme kuralları, sıradan işlevlerin kurallarına bağlıdır. Daha fazla bilgi için bkz . İşlev Şablonu Çağrılarının Aşırı Yükleme Çözümlemesi.

Tür parametreleri

Yukarıdaki şablondaminimum, T tür parametresinin, const ve başvuru niteleyicilerinin eklendiği işlev çağrısı parametrelerinde kullanılana kadar hiçbir şekilde nitelenmediğini unutmayın.

Tür parametrelerinin sayısı için pratik bir sınır yoktur. Birden çok parametreyi virgülle ayırın:

template <typename T, typename U, typename V> class Foo{};

anahtar sözcüğü class bu bağlamdaki ile typename eşdeğerdir. Önceki örneği şu şekilde ifade edebilirsiniz:

template <class T, class U, class V> class Foo{};

Rastgele sayıda sıfır veya daha fazla tür parametresi alan bir şablon tanımlamak için üç nokta işlecini (...) kullanabilirsiniz:

template<typename... Arguments> class vtclass;

vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;

Yerleşik veya kullanıcı tanımlı herhangi bir tür, tür bağımsız değişkeni olarak kullanılabilir. Örneğin, Standart Kitaplık'ta std::vector kullanarak , , doublestd::string, MyClass, constMyClass*, MyClass&ve benzeri türdeki intdeğişkenleri depolayabilirsiniz. Şablonları kullanırken birincil kısıtlama, tür bağımsız değişkeninin tür parametrelerine uygulanan tüm işlemleri desteklemesi gerektiğidir. Örneğin, bu örnekte olduğu gibi kullanarak MyClass öğesini çağırırsakminimum:

class MyClass
{
public:
    int num;
    std::wstring description;
};

int main()
{
    MyClass mc1 {1, L"hello"};
    MyClass mc2 {2, L"goodbye"};
    auto result = minimum(mc1, mc2); // Error! C2678
}

İşleç için < aşırı yükleme sağlamadığından bir derleyici hatası oluşturulurMyClass.

Belirli bir şablon için tür bağımsız değişkenlerinin tümünün aynı nesne hiyerarşisine ait olması gerekmez, ancak böyle bir kısıtlamayı zorunlu kılan bir şablon tanımlayabilirsiniz. Nesne odaklı teknikleri şablonlarla birleştirebilirsiniz; örneğin, türetilmiş* bir vektör<Tabanında*> depolayabilirsiniz. Bağımsız değişkenlerin işaretçi olması gerektiğini unutmayın

vector<MyClass*> vec;
   MyDerived d(3, L"back again", time(0));
   vec.push_back(&d);

   // or more realistically:
   vector<shared_ptr<MyClass>> vec2;
   vec2.push_back(make_shared<MyDerived>());

ve diğer standart kitaplık kapsayıcılarının öğelerine T uyguladığı temel gereksinimlerstd::vector, kopya atanabilir ve kopya oluşturulabilir olmasıdırT.

Tür olmayan parametreler

C# ve Java gibi diğer dillerdeki genel türlerden farklı olarak, C++ şablonları değer parametreleri olarak da adlandırılan tür olmayan parametreleri destekler. Örneğin, standart kitaplıktaki std::array sınıfına benzer bir örnekte olduğu gibi bir dizinin uzunluğunu belirtmek için sabit bir tamsayı değeri sağlayabilirsiniz:

template<typename T, size_t L>
class MyArray
{
    T arr[L];
public:
    MyArray() { ... }
};

Şablon bildirimindeki söz dizimine dikkat edin. Değersize_t, derleme zamanında şablon bağımsız değişkeni olarak geçirilir ve veya bir constexpr ifade olmalıdırconst. Bunu şu şekilde kullanırsınız:

MyArray<MyClass*, 10> arr;

İşaretçiler ve başvurular dahil olmak üzere diğer değer türleri türü olmayan parametreler olarak geçirilebilir. Örneğin, şablon kodu içindeki bir işlemi özelleştirmek için bir işleve veya işlev nesnesine işaretçi geçirebilirsiniz.

Tür olmayan şablon parametreleri için tür kesintisi

Visual Studio 2017 ve sonraki sürümlerde ve mod veya sonraki sürümlerde /std:c++17 , derleyici ile autobildirilen tür olmayan bir şablon bağımsız değişkeninin türünü gösterir:

template <auto x> constexpr auto constant = x;

auto v1 = constant<5>;      // v1 == 5, decltype(v1) is int
auto v2 = constant<true>;   // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>;    // v3 == 'a', decltype(v3) is char

Şablon parametresi olarak şablonlar

Şablon bir şablon parametresi olabilir. Bu örnekte, MyClass2'nin iki şablon parametresi vardır: tür adı parametresi T ve şablon parametresi Arr:

template<typename T, template<typename U, int I> class Arr>
class MyClass2
{
    T t; //OK
    Arr<T, 10> a;
    U u; //Error. U not in scope
};

Arr parametresinin gövdesi olmadığından parametre adları gerekli değildir. Aslında, gövdesinden Arr'ın tür adına veya sınıf parametresi adlarına başvurmak bir hatadırMyClass2. Bu nedenle, bu örnekte gösterildiği gibi Arr'ın tür parametre adları atlanabilir:

template<typename T, template<typename, int> class Arr>
class MyClass2
{
    T t; //OK
    Arr<T, 10> a;
};

Varsayılan şablon bağımsız değişkenleri

Sınıf ve işlev şablonları varsayılan bağımsız değişkenlere sahip olabilir. Bir şablonun varsayılan bağımsız değişkeni olduğunda, şablonu kullandığınızda belirtilmemiş olarak bırakabilirsiniz. Örneğin, std::vector şablonu ayırıcı için varsayılan bir bağımsız değişkene sahiptir:

template <class T, class Allocator = allocator<T>> class vector;

Çoğu durumda varsayılan std::allocator sınıfı kabul edilebilir olduğundan aşağıdaki gibi bir vektör kullanırsınız:

vector<int> myInts;

Ancak gerekirse aşağıdaki gibi bir özel ayırıcı belirtebilirsiniz:

vector<int, MyAllocator> ints;

Birden çok şablon bağımsız değişkeni için, ilk varsayılan bağımsız değişkenden sonraki tüm bağımsız değişkenlerin varsayılan bağımsız değişkenleri olmalıdır.

Parametreleri varsayılan olarak atanmış bir şablon kullanırken boş açılı ayraç kullanın:

template<typename A = int, typename B = double>
class Bar
{
    //...
};
...
int main()
{
    Bar<> bar; // use all default type arguments
}

Şablon uzmanlığı

Bazı durumlarda, bir şablonun herhangi bir tür için tam olarak aynı kodu tanımlaması mümkün veya istenen bir durum değildir. Örneğin, yalnızca tür bağımsız değişkeni bir işaretçi, std::wstring veya belirli bir temel sınıftan türetilmiş bir tür olduğunda yürütülecek bir kod yolu tanımlamak isteyebilirsiniz. Böyle durumlarda, şablonun bu tür için bir uzmanlığını tanımlayabilirsiniz. Kullanıcı bu türdeki şablonun örneğini oluşturduğunda, derleyici sınıfını oluşturmak için özelleştirmeyi kullanır ve diğer tüm türler için derleyici daha genel şablonu seçer. Tüm parametrelerin özelleştirilmiş olduğu uzmanlıklar tam uzmanlıklardır. Parametrelerin yalnızca bazıları özelleştirilmişse, kısmi özelleştirme olarak adlandırılır.

template <typename K, typename V>
class MyMap{/*...*/};

// partial specialization for string keys
template<typename V>
class MyMap<string, V> {/*...*/};
...
MyMap<int, MyClass> classes; // uses original template
MyMap<string, MyClass> classes2; // uses the partial specialization

Her özel tür parametresi benzersiz olduğu sürece bir şablonda herhangi bir sayıda uzmanlık bulunabilir. Yalnızca sınıf şablonları kısmen özelleştirilebilir. Bir şablonun tüm tam ve kısmi uzmanlıkları özgün şablonla aynı ad alanında bildirilmelidir.

Daha fazla bilgi için bkz . Şablon Özelleştirmesi.