<Unsigned int N> şablonu ne anlama geliyor?


121

Bir şablon bildirirken, bu tür bir koda sahip olmaya alışkınım:

template <class T>

Ancak bu soruda şunları kullandılar:

template <unsigned int N>

Derlendiğini kontrol ettim. Ama bu ne demek? Tip dışı bir parametre mi? Ve eğer öyleyse, herhangi bir tür parametresi olmayan bir şablona nasıl sahip olabiliriz?

Yanıtlar:


148

Bir sınıfı bir türden ziyade bir tamsayı üzerinde şablonlamak tamamen mümkündür. Şablonlu değeri bir değişkene atayabilir veya başka bir tamsayı değişmezi ile yapabileceğimiz şekilde değiştirebiliriz:

unsigned int x = N;

Aslında, derleme zamanında ( Wikipedia'dan ) değerlendiren algoritmalar oluşturabiliriz :

template <int N>
struct Factorial 
{
     enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}

11
Sizin static constexpr intyerine type da kullanabilirsiniz enum. Öyleyse Factorial<0>şablon olabilir static constexpr int value = 1ve template <int N> struct Factorialolabilirstatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo

@bobobobo buna C ++ 11 ve constexpr.
Justin Meiners

154

Evet, tür olmayan bir parametredir. Birkaç çeşit şablon parametreniz olabilir

  • Tür Parametreleri.
    • Türleri
    • Şablonlar (yalnızca sınıflar ve takma ad şablonları, işlevler veya değişken şablonlar yok)
  • Tip Dışı Parametreler
    • İşaretçiler
    • Referanslar
    • İntegral sabit ifadeler

Sahip olduklarınız son türden. Bu bir derleme zaman sabitidir (sabit ifade olarak adlandırılır) ve tamsayı veya numaralandırma türündedir. Standartta aradıktan sonra, sınıf şablonlarını türler bölümüne taşımak zorunda kaldım - şablonlar tür olmasa da. Ancak yine de bu türleri açıklamak amacıyla bunlara tip parametreleri denir. İşaretçileriniz (ve ayrıca üye işaretçileriniz) ve harici bağlantıya sahip nesnelere / işlevlere (diğer nesne dosyalarından bağlanabilenler ve tüm programda adresi benzersiz olanlar) referanslara sahip olabilirsiniz. Örnekler:

Şablon türü parametresi:

template<typename T>
struct Container {
    T t;
};

// pass type "long" as argument.
Container<long> test;

Şablon tamsayı parametresi:

template<unsigned int S>
struct Vector {
    unsigned char bytes[S];
};

// pass 3 as argument.
Vector<3> test;

Şablon işaretçi parametresi (bir işleve bir işaretçi iletme)

template<void (*F)()>
struct FunctionWrapper {
    static void call_it() { F(); }
};

// pass address of function do_it as argument.
void do_it() { }
FunctionWrapper<&do_it> test;

Şablon referans parametresi (bir tamsayı geçirme)

template<int &A>
struct SillyExample {
    static void do_it() { A = 10; }
};

// pass flag as argument
int flag;
SillyExample<flag> test;

Şablon şablon parametresi.

template<template<typename T> class AllocatePolicy>
struct Pool {
    void allocate(size_t n) {
        int *p = AllocatePolicy<int>::allocate(n);
    }
};

// pass the template "allocator" as argument. 
template<typename T>
struct allocator { static T * allocate(size_t n) { return 0; } };
Pool<allocator> test;

Parametre içermeyen bir şablon mümkün değildir. Ancak açık bir argüman içermeyen bir şablon mümkündür - varsayılan argümanları vardır:

template<unsigned int SIZE = 3>
struct Vector {
    unsigned char buffer[SIZE];
};

Vector<> test;

Sözdizimsel olarak, template<>parametresiz bir şablon yerine açık bir şablon uzmanlığını işaretlemek için ayrılmıştır:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};

Johannes, şablonlar "türler" altında mı dosyalanır? Hangi türlerden yapılabileceklerini sanıyordum ama kendileri değil mi?
sbi

@sbi açıklamaya bakın: "Standartta aradıktan sonra, sınıf şablonlarını türler bölümüne taşımak zorunda kaldım - şablonlar tür olmasa bile. Ancak yine de bu türleri açıklamak amacıyla bunlara tür parametreleri denir. ". 14.1 / 2'deki 126 numaralı dipnot öyle diyor. Tip dışı parametreleri bir değer / referans bildiren ve tür parametreleri bir tür adı veya şablon adı bildiren bir şey yapmak için yapılan bir sınıflandırmadır.
Johannes Schaub -

@ JohannesSchaub-litb yani std :: string ile şablon yazmanın bir yolu yok mu? şablon <std :: string S> sınıfı gibi her farklı dizge için benzersiz bir kimlik oluşturmak için içinde bazı statik sayaçlar var mı? int için hashing dizesi maalesef tek yol olacaktır, değil mi?
relaxxx

1
Bu cevabın şablon sınıfı üye nesneleriyle tamamlandığını görmek isterim, örn. Şablon <typename C, typename R, typename P1, typename P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling

SillyExampleİle kod parçası GCC 4.8.4 tarafından derlenemez. İlk hata the value of ‘flag’ is not usable in a constant expression. Başka hatalar da var
HEKTO

17

Sınıfınızı 'imzasız int' temelinde şablonlaştırırsınız.

Misal:

template <unsigned int N>
class MyArray
{
    public:
    private:
        double    data[N]; // Use N as the size of the array
};

int main()
{
    MyArray<2>     a1;
    MyArray<2>     a2;

    MyArray<4>     b1;

    a1 = a2;  // OK The arrays are the same size.
    a1 = b1;  // FAIL because the size of the array is part of the
              //      template and thus the type, a1 and b1 are different types.
              //      Thus this is a COMPILE time failure.
 }

15

Bir şablon sınıfı makro gibidir, ancak çok daha az kötüdür.

Bir şablonu bir makro olarak düşünün. Bir şablon kullanarak bir sınıf (veya işlev) tanımladığınızda, şablonun parametreleri bir sınıf (veya işlev) tanımıyla değiştirilir.

Aradaki fark, parametrelerin "türlere" sahip olması ve derleme sırasında, parametrelerin işlevlere olduğu gibi, geçirilen değerlerin kontrol edilmesidir. Geçerli türler, int ve char gibi normal C ++ türlerinizdir. Bir şablon sınıfını başlattığınızda, belirttiğiniz türde bir değer iletirsiniz ve şablon sınıf tanımının yeni bir kopyasında bu değer, parametre adının orijinal tanımda olduğu yerde değiştirilir. Tıpkı bir makro gibi.

Ayrıca parametreler için " class" veya " typename" türlerini de kullanabilirsiniz (gerçekten aynıdırlar). Bu türlerden birinin parametresiyle, değer yerine bir tür adı geçebilirsiniz. Daha önce olduğu gibi, parametre adının şablon sınıf tanımında olduğu her yerde, yeni bir örnek oluşturduğunuzda, geçtiğiniz her tür olur. Bu, bir şablon sınıfı için en yaygın kullanımdır; C ++ şablonları hakkında herhangi bir şey bilen herkes bunun nasıl yapılacağını bilir.

Şu şablon sınıfı örnek kodunu göz önünde bulundurun:

#include <cstdio>
template <int I>
class foo
{
  void print()
  {
    printf("%i", I);
  }
};

int main()
{
  foo<26> f;
  f.print();
  return 0;
}

İşlevsel olarak bu makro kullanan kodla aynıdır:

#include <cstdio>
#define MAKE_A_FOO(I) class foo_##I \
{ \
  void print() \
  { \
    printf("%i", I); \
  } \
};

MAKE_A_FOO(26)

int main()
{
  foo_26 f;
  f.print();
  return 0;
}

Elbette, şablon sürümü milyarlarca kez daha güvenli ve daha esnektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.