Neden yapıcıdan şablon parametresi çıkarılmıyor?


103

Bugünkü sorum oldukça basit: Derleyici neden işlev parametrelerinden yapabildiği gibi sınıf kurucularından şablon parametreleri çıkaramıyor? Örneğin, aşağıdaki kod neden geçerli olamadı:

template<typename obj>
class Variable {
      obj data;
      public: Variable(obj d)
              {
                   data = d;
              }
};

int main()
{
    int num = 2;
    Variable var(num); //would be equivalent to Variable<int> var(num),
    return 0;          //but actually a compile error
}

Dediğim gibi, bunun geçerli olmadığını anlıyorum, öyleyse sorum neden öyle değil? Buna izin vermek büyük sözdizimsel boşluklar yaratır mı? Bu işlevselliği istemeyen bir durum var mı (bir tür çıkarımının sorunlara neden olacağı)? İşlevler için şablon çıkarımına izin vermenin arkasındaki mantığı anlamaya çalışıyorum, ancak uygun şekilde oluşturulmuş sınıflar için değil.


Drahakar ve Pitis cevabını (en azından) neden işe yaramayacağına dair iyi bir karşı örnek olarak derlemeye (en azından) birini davet ediyorum (bunu yapıyorum, şimdi değil)
jpinto3912

2
Ayrıca bunun kolayca çözülebileceğini unutmayıntemplate<class T> Variable<T> make_Variable(T&& p) {return Variable<T>(std::forward<T>(p));}
Mooing Duck

3
İstediğinizi sıralayabilirsiniz var = Variable <decltype (n)> (n);
QuentinUK

18
C ++ 17 buna izin verecektir! Bu öneri kabul edildi: open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html
underscore_d

1
@underscore_d Mükemmel! Zaman hakkında! Benim için doğal hissettirdi, bu şekilde çalışması gerekiyordu ve işe yaramaması rahatsızlık kaynağıydı.
amdn

Yanıtlar:


47

Bunun geçerli olmadığını düşünüyorum çünkü kurucu her zaman sınıfın tek giriş noktası değildir (kopya yapıcı ve operatör = hakkında konuşuyorum). Diyelim ki sınıfınızı şu şekilde kullanıyorsunuz:

MyClass m(string s);
MyClass *pm;
*pm = m;

Ayrıştırıcının hangi şablon tipinin Sınıfım pm olduğunu bilmesi bu kadar açık olup olmayacağından emin değilim;

Söylediğim şeyin mantıklı olup olmadığından emin değilim ama yorum eklemekten çekinmeyin, bu ilginç bir soru.

C ++ 17

C ++ 17'nin yapıcı argümanlarından tür çıkarımı olacağı kabul edilir.

Örnekler:

std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);

Kabul edilen kağıt .


8
Bu aslında hiç düşünmediğim harika bir nokta. İşaretçinin türe özgü olması gerektiği gerçeğinin etrafında herhangi bir yol göremiyorum (yani, MyClass <string> * pm olması gerekir). Durum buysa, yapmanız gereken tek şey kendinizi somutlama sırasında türü belirlemekten kurtarmaktır; fazladan çalışmanın birkaç yalnızca karakteri (ve yalnızca nesne yukarıda belirtildiği gibi yığın üzerinde değil, yığın üzerinde yapılmışsa). Her zaman sınıf çıkarımının sözdizimsel bir solucan kutusu açabileceğinden şüphelenmiştim ve bence bu olabilir.
GRB

2
Yapıcılardan şablon parametresi çıkarımına izin vermenin , ikinci satırınızda olduğu gibi, kurucu çağrıları olmadan özelleştirilmiş olmayan bildirimlere izin vermeyi nasıl gerektireceğini tam olarak anlamıyorum . Yani, MyClass *pmbeyan edilen bir işlevin template <typename T> void foo();açık uzmanlaşma olmadan çağrılamamasıyla aynı nedenle burada geçersiz olacaktır .
Kyle Strand

3
@KyleStrand Evet, 'sınıf şablonu argümanları kendi oluşturucularından çıkarılamaz çünkü [herhangi bir kurucu kullanmayan örnek] ' diyerek , bu cevap tamamen alakasızdır. Kabul edildiğine gerçekten inanamıyorum, +29'a ulaştım, birinin göze batan sorunu fark etmesi 6 yıl sürdü ve 7 yıl boyunca tek bir olumsuz oy vermeden oturdum. Okurken başka kimse düşünmüyor mu, yoksa ...?
underscore_d

1
@underscore_d Şu anda olduğu gibi, bu cevabın "bu teklifle ilgili bazı sorunlar olabileceğini; az önce söylediklerimin mantıklı olup olmadığından emin değilim (!), yorum yapmaktan çekinmeyin (!!); ve oh bu arada bu, C ++ 17'nin tam olarak nasıl çalışacağıdır. "
Kyle Strand

1
@KyleStrand Ah evet, bu da fark ettiğim ancak diğer eğlenceler arasında bahsetmeyi unuttuğum başka bir konu. C ++ 17 ile ilgili düzenleme OP tarafından yapılmamıştı ... ve IMO onaylanmamalıydı, ancak yeni bir cevap olarak gönderilmeliydi: gönderi yapılsa bile 'yazının anlamını değiştirdiği için reddedilebilirdi Başlamak anlamsız değildi ... Düzenlemenin tamamen yeni bölümlerin adil bir oyundu ve kesinlikle daha az sert düzenlemelerin reddedildiğinin farkında değildim, ama galiba hangi gözden geçirenler açısından çekilişin şansı bu.
underscore_d

27

Başkalarının hitap ettiği nedenlerle istediğinizi yapamazsınız, ancak bunu yapabilirsiniz:

template<typename T>
class Variable {
    public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
  return Variable<T>(instance);
}

tüm niyet ve amaçlar için istediğiniz şey aynıdır. Kapsüllemeyi seviyorsanız make_variable'ı statik bir üye işlevi yapabilirsiniz. İnsanların adlandırılmış kurucu dediği şey budur. Yani sadece istediğinizi yapmakla kalmaz, neredeyse istediğiniz şey denir: derleyici şablon parametresini (adlandırılmış) kurucudan çıkarır.

Not: makul herhangi bir derleyici, aşağıdaki gibi bir şey yazdığınızda geçici nesneyi optimize edecektir.

auto v = make_variable(instance);

6
Böyle bir durumda işlevi statik üye yapmanın özellikle yararlı olmadığını belirtmek isteriz, çünkü bunun için bir sınıfın onu yine de çağırması için şablon argümanını belirtmeniz gerekir, böylece onu çıkarmanın bir anlamı olmaz.
Predelnik

3
Ve C ++ 11'de daha da iyisi auto v = make_variable(instance), türü belirtmek zorunda
Claudiu

1
Evet, make işlevini staticüye olarak ilan etme fikrine karşı lol ... bunu bir saniye düşün. Bunun yanı sıra: free make fonksiyonları gerçekten de çözümdü, ancak çok fazla gereksiz ortak metin var, siz yazarken , derleyicinin tekrar ettiğiniz tüm bilgilere erişimi olduğu için, gerekmediğini biliyorsunuz . .. ve çok şükür C ++ 17 bunu kanonize ediyor.
underscore_d

21

2016'nın aydınlanmış çağında, bu soru sorulduğundan beri iki yeni standart ve hemen köşemizde yeni bir standart ile, bilinmesi gereken en önemli şey , C ++ 17 standardını destekleyen derleyicilerin kodunuzu olduğu gibi derleyeceğidir. .

C ++ 17'deki sınıf şablonları için şablon bağımsız değişkeni çıkarımı

Burada (kabul edilen cevaba Olzhas Zhumabek tarafından yapılan bir düzenlemenin izniyle), standarttaki ilgili değişiklikleri detaylandıran kağıt bulunmaktadır.

Diğer cevaplardan gelen endişeleri ele almak

Mevcut en yüksek puanlı cevap

Bu yanıt, "kurucuyu kopyala operator=" nın doğru şablon uzmanlıklarını bilmediğine işaret eder.

Standart kopyalama yapıcı çünkü bu, saçmalık operator= var sadece bir için bilinen şablon tipi:

template <typename T>
class MyClass {
    MyClass(const MyClass&) =default;
    ... etc...
};

// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm;   // WHAT IS THIS?
*pm = m;

Ben açıklamalarda belirtildiği gibi burada, orada hiçbir neden için MyClass *pmbirlikte veya çıkarımın yeni formu olmadan yasal bir deklarasyon olmak: MyClass bir tür değil o bir işaretçi ilan etme mantıklı değil bu yüzden, (öyle bir şablon var) yazın MyClass. İşte örneği düzeltmenin olası bir yolu:

MyClass m(string("blah blah blah"));
decltype(m) *pm;               // uses type inference!
*pm = m;

Burada pmise zaten çıkarım önemsiz doğru tipte ve böylece. Dahası, kopya yapıcıyı çağırırken türleri yanlışlıkla karıştırmak imkansızdır :

MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));

Burada pmbir kopyasına işaretçi olacak m. Burada, MyClassgelen kopya inşa ediliyor m-ki tiptedir MyClass<string>(ve değil varolmayan Çeşidi MyClass). Bu nedenle, bir noktada pmçeşidini çıkarılır, var olan bir kalıp-türü bir bilmek yeterli bilgi m, ve bu yüzden de kalıp-türü pmvardır string.

Ayrıca, aşağıdakiler her zaman bir derleme hatası doğurur :

MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;

Bunun nedeni, kopya oluşturucunun bildiriminin şablonlu olmamasıdır :

MyClass(const MyClass&);

Burada, kopya-yapıcı bağımsız değişkeninin şablon türü, genel olarak sınıfın şablon türü ile eşleşir ; yani, ne zaman MyClass<string>somutlaştırılır, MyClass<string>::MyClass(const MyClass<string>&);onunla somutlaştırılır ve ne zaman MyClass<int>somutlaştırılırsa MyClass<int>::MyClass(const MyClass<int>&);somutlaştırılır. Açıkça belirtilmedikçe veya şablonlaştırılmış bir kurucu bildirilmedikçe, derleyicinin somutlaştırması için bir neden yoktur MyClass<int>::MyClass(const MyClass<string>&);, bu açıkça uygunsuz olacaktır.

Cevap Cătălin Pitiș

Pitiș, çıkarım yapan bir örnek verir Variable<int>ve Variable<double>ardından şunu belirtir:

Kodda iki farklı tür için (Değişken ve Değişken) aynı tür adıma (Değişken) sahibim. Öznel bakış açıma göre, kodun okunabilirliğini oldukça fazla etkiliyor.

Önceki örnekte belirtildiği gibi , yeni özellik sözdizimsel olarak öyle görünmesine rağmen , Variablekendisi bir tür adı değildir .

Pitiș daha sonra uygun çıkarıma izin verecek bir kurucu verilmemesi durumunda ne olacağını sorar. Cevap, çıkarıma izin verilmediğidir, çünkü çıkarım yapıcı çağrısı tarafından tetiklenir . Bir kurucu çağrısı olmadan, hiçbir çıkarım yoktur .

Bu, fooburada hangi sürümün çıkarıldığını sormaya benzer :

template <typename T> foo();
foo();

Cevap, belirtilen nedenle bu kodun yasa dışı olmasıdır.

MSalter'ın cevabı

Anladığım kadarıyla, önerilen özellik hakkında meşru bir endişeyi ortaya çıkarmanın tek cevabı bu.

Örnek şudur:

Variable var(num);  // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?

Buradaki anahtar soru, derleyici burada türden türetilen yapıcıyı mı yoksa kopya oluşturucuyu mu seçiyor ?

Kodu denediğimizde, kopya yapıcısının seçildiğini görebiliriz. Örneği genişletmek için :

Variable var(num);          // infering ctor
Variable var2(var);         // copy ctor
Variable var3(move(var));   // move ctor
// Variable var4(Variable(num));     // compiler error

Teklifin ve standardın yeni versiyonunun bunu nasıl belirttiğinden emin değilim; henüz anlamadığım yeni bir standart olan "kesinti kılavuzları" tarafından belirleniyor gibi görünüyor.

Ayrıca var4kesintinin neden yasa dışı olduğundan da emin değilim ; g ++ 'dan derleyici hatası, ifadenin bir işlev bildirimi olarak ayrıştırıldığını gösteriyor gibi görünüyor.


Ne harika, ayrıntılı bir cevap! var4sadece "en sinir bozucu çözümleme" durumudur (şablon argümanı kesintisi ile ilgili değildir). Bunun için sadece fazladan parantez kullanırdık, ancak bu günlerde inşaatı açık bir şekilde belirtmek için parantez kullanmanın olağan bir tavsiye olduğunu düşünüyorum.
Sumudu Fernando

@SumuduFernando Teşekkürler! Bunun Variable var4(Variable(num));bir işlev bildirimi olarak değerlendirildiğini mi söylüyorsunuz ? Öyleyse, neden Variable(num)geçerli bir parametre belirtimi?
Kyle Strand

@SumuduFernando Boşver, bunun geçerli olduğu hakkında hiçbir fikrim yoktu: coliru.stacked-crooked.com/a/98c36b8082660941
Kyle Strand

11

Hala eksik: Aşağıdaki kodu oldukça belirsiz hale getiriyor:

int main()
{
    int num = 2;
    Variable var(num);  // If equivalent to Variable<int> var(num),
    Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}

Başka bir iyi nokta. Değişken (Değişken <obj> d) tanımlı bir kopya oluşturucu olduğunu varsayarsak, bir tür önceliğin oluşturulması gerekir.
GRB

1
Veya alternatif olarak, Pitis'in cevabıyla ilgili olarak önerdiğim gibi, derleyicinin tanımlanmamış bir şablon parametresi hatasını tekrar atmasını sağlayın. Bununla birlikte, bu rotayı seçerseniz, çıkarımın problemsiz (hatalar) gerçekleşebileceği zamanların sayısı gittikçe küçülüyor.
GRB

Bu aslında ilginç bir nokta ve (cevabımda da belirttiğim gibi) kabul edilen C ++ 17 önerisinin bunu nasıl çözeceğinden henüz emin değilim.
Kyle Strand

9

Derleyicinin istediğinizi desteklediğini varsayarsak. O halde bu kod geçerlidir:

Variable v1( 10); // Variable<int>

// Some code here

Variable v2( 20.4); // Variable<double>

Şimdi, iki farklı tür için (Değişken ve Değişken) kodda aynı tür adına (Değişken) sahibim. Öznel bakış açıma göre, kodun okunabilirliğini oldukça fazla etkiliyor. Aynı ad alanında iki farklı tür için aynı tür adının olması bana yanıltıcı geliyor.

Daha sonra güncelleme: Dikkate alınması gereken başka bir şey: kısmi (veya tam) şablon uzmanlığı.

Ya Değişken'i uzmanlaştırırsam ve beklediğiniz gibi bir kurucu sağlamazsam?

Yani sahip olacaktım:

template<>
class Variable<int>
{
// Provide default constructor only.
};

O zaman bende kod var:

Variable v( 10);

Derleyici ne yapmalı? Değişken olduğunu anlamak için genel Değişken sınıf tanımını kullanın, sonra Değişken'in tek bir parametre yapıcısı sağlamadığını keşfedin.


1
Daha kötüsü: Ya yalnızca Değişken <int> :: Değişken (float) varsa? Artık Değişken (1f) 'yi çıkarmanın iki yolu var ve Değişken (1)' i çıkarmanın yolu yok.
MSalters

Bu iyi bir nokta, ancak döküm ile kolayca aşılabilir: Değişken v1 ((çift) 10)
jpinto3912

Kod okunabilirliğinin öznel bir konu olduğuna katılıyorum, ancak şablon uzmanlığı hakkında söylediklerinize% 100 katılıyorum. Çözüm muhtemelen tanımsız bir şablon parametresi hatası vermek olacaktır (derleyici <int> uzmanlığına baktığında ve geçerli bir kurucu görmediğinde, hangi şablonu kullanmak istediğinize dair hiçbir fikri olmadığını ve açıkça belirtmeniz gerektiğini söylemesini sağlayın) ancak Güzel bir çözüm olmadığına katılıyorum. Bunu, ele alınması gereken (ancak sonuçları kabul edilirse çözülebilecek) başka bir büyük sözdizimsel boşluk olarak ekleyeceğim.
GRB

4
@ jpinto3912 - noktayı kaçırıyorsunuz. Derleyicinin HERHANGİ bir ctor Değişken <T> :: Değişken belirsiz bir ctor sağlayıp sağlamadığını kontrol etmek için TÜM olası Değişken <T> 'leri somutlaştırması gerekir. Belirsizlikten kurtulmak sorun değil - basit bir şekilde Değişkeni <çift> kendiniz somutlaştırın, eğer istediğiniz buysa. Bunu imkansız kılan en başta bu belirsizliği bulmaktır.
MSalters

6

C ++ 03 ve C ++ 11 standardı, yapılandırıcıya iletilen parametrelerden şablon bağımsız değişkeninin çıkarılmasına izin vermez.

Ancak "Yapıcılar için şablon parametresi kesintisi" için bir öneri var, bu nedenle yakında istediğiniz şeyi elde edebilirsiniz. Düzenleme: gerçekten, bu özellik C ++ 17 için onaylanmıştır.

Bkz: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html ve http://www.open-std.org/jtc1/sc22/wg21/docs/ kağıtları / 2015 / p0091r0.html


Özellik C ++ 17'ye eklenmiştir, ancak "yakında" 6 ila 8 yıllık bir zaman çerçevesi için geçerli değilse. ;)
ChetS

2

Bir çok sınıf yapıcı parametrelerine bağlı değildir. Yalnızca bir yapıcıya sahip olan ve bu yapıcının türüne (türlerine) göre parametreleştiren yalnızca birkaç sınıf vardır.

Gerçekten şablon çıkarımına ihtiyacınız varsa, bir yardımcı işlev kullanın:

template<typename obj>
class Variable 
{
      obj data;
public: 
      Variable(obj d)
      : data(d)
      { }
};

template<typename obj>
inline Variable<obj> makeVariable(const obj& d)
{
    return Variable<obj>(d);
}

1
Elbette bu işlevsellik yalnızca bazı sınıflar için yararlı olabilir, ancak aynı şey işlev çıkarımı için de söylenebilir. Tüm şablonlu işlevler parametrelerini bağımsız değişken listesinden almaz, yine de bunu yapan işlevler için çıkarıma izin veririz.
GRB

1

Türlerin çıkarılması mevcut C ++ 'da şablon işlevleriyle sınırlıdır, ancak uzun zamandır diğer bağlamlarda tür çıkarımının çok yararlı olacağı anlaşılmıştır. Dolayısıyla C ++ 0x'ler auto.

İken tam olarak ne, C ++ 0x ile oldukça yakın alabilirsiniz aşağıdaki gösterileri mümkün olmayacaktır önermek:

template <class X>
Variable<typename std::remove_reference<X>::type> MakeVariable(X&& x)
{
    // remove reference required for the case that x is an lvalue
    return Variable<typename std::remove_reference<X>::type>(std::forward(x));
}

void test()
{
    auto v = MakeVariable(2); // v is of type Variable<int>
}

0

Derleyicinin kolayca tahmin edebileceği haklısınız, ancak bildiğim kadarıyla standart veya C ++ 0x değil, bu nedenle derleyici sağlayıcıları bu özelliği eklemeden önce en az 10 yıl daha (ISO standartları sabit dönüş oranı) beklemeniz gerekecek


Yaklaşan standartta bu doğru değil, otomatik anahtar kelime tanıtılacak. Bu konudaki James Hopkins yazısına bir göz atın. stackoverflow.com/questions/984394/… . C ++ 0x'de bunun nasıl mümkün olacağını gösteriyor.
ovanes

1
Kendimi düzeltmek için, auto anahtar kelimesi de mevcut standartta mevcuttur, ancak farklı bir amaç için.
ovanes

Görünüşe göre 8 yıl olacak (bu cevabın verildiği andan itibaren) ... bu yüzden 10 yıl, bu arada iki standart olmasına rağmen kötü bir tahmin değildi!
Kyle Strand

-1

Soruna herkesin aşina olması gereken bir sınıfa göre bakalım - std :: vector.

İlk olarak, vektörün çok yaygın bir kullanımı, parametre almayan yapıcıyı kullanmaktır:

vector <int> v;

Bu durumda, açıkça hiçbir çıkarım yapılamaz.

İkinci bir yaygın kullanım, önceden boyutlandırılmış bir vektör oluşturmaktır:

vector <string> v(100);

Burada çıkarım kullanılmışsa:

vector v(100);

dizgelerin değil, bir ints vektörü elde ederiz ve muhtemelen boyutlandırılmamış!

Son olarak, birden çok parametre alan yapıcıları düşünün - "çıkarımla":

vector v( 100, foobar() );      // foobar is some class

Çıkarım için hangi parametre kullanılmalıdır? Derleyiciye ikincisi olması gerektiğini söylemenin bir yoluna ihtiyacımız var.

Vektör kadar basit bir sınıf için tüm bu problemlerle, çıkarımın neden kullanılmadığını anlamak kolaydır.


3
Sanırım fikri yanlış anlıyorsun. Yapıcılar için tür çıkarımı, yalnızca şablon türü yapıcının bir parçasıysa gerçekleşir. Vektörün <typename T> şablon tanımlama şablonuna sahip olduğunu varsayın. Örneğiniz bir sorun değil çünkü vektörün kurucusu vektör (T boyutu) değil vektör (int boyut) olarak tanımlanacaktır. Sadece vektör (T boyutu) durumunda herhangi bir sonuç çıkarılabilir; ilk örnekte, derleyici T'nin tanımsız olduğunu söyleyen bir hata verecektir. İşlev şablonu çıkarımının nasıl çalıştığı ile özde aynıdır.
GRB

Bu nedenle, yalnızca tek bir parametresi olan ve bu parametrenin bir şablon parametre türü olduğu kurucular için gerçekleşir? Bu, gözden kaybolan az sayıda örnek gibi görünüyor.

Bunun tek bir parametre olması gerekmez. Örneğin, bir vektör yapıcısı olabilir (int size, T firstElement). Bir şablonun birden çok parametresi varsa (şablon <tür adı T, tür adı U>), bir Holder :: Holder (T firstObject, U secondObject) olabilir. Bir şablonun birden fazla parametresi varsa ancak kurucu bunlardan yalnızca birini alırsa, örneğin Tutucu (U secondObject), bu durumda T'nin her zaman açıkça belirtilmesi gerekir. Kuralların, mümkün olduğunca işlev şablonu çıkarımına benzer olması amaçlanacaktır.
GRB

-2

Ctor'u bir şablon yapmak Değişkenin yalnızca bir formu olabilir, ancak çeşitli ctor'lar olabilir:

class Variable {
      obj data; // let the compiler guess
      public:
      template<typename obj>
      Variable(obj d)
       {
           data = d;
       }
};

int main()
{
    int num = 2;
    Variable var(num);  // Variable::data int?

    float num2 = 2.0f;
    Variable var2(num2);  // Variable::data float?
    return 0;         
}

Görmek? Birden fazla Değişken :: veri üyemiz olamaz.


Bu hiçbir senaryoda mantıklı olmaz. obj verileri açısından obj, bu sınıf artık bir şablon olmadığından tanımsızdır. Böyle bir kod her iki şekilde de geçersiz olacaktır.
GRB

Tanımladığınız derleyici davranışını istedim, bu yüzden ilginç bulabileceğiniz bu kısıtlamayı (benim durumumda) atlamanın bir yolunu buldum, stackoverflow.com/questions/228620/garbage-collection-in-c-why/…
Nick Dandoulakis

-2

Bkz C ++ Şablon Argüman Kesintisi Bu konuda daha fazla bilgi için.


4
Bu makaleyi daha önce okudum ve söylediklerim hakkında pek konuşulmadı. Yazarın sınıflarla ilgili argüman çıkarımından bahsettiği tek zaman, makalenin başında bunun yapılamayacağını söylediği zamandır;) - ilgili olduğunu düşündüğünüz bölümleri belirtebilirseniz, Bunu gerçekten takdir ediyorum.
GRB
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.