Değiştirme hatası bir hata değildir - Substitution failure is not an error

Değiştirme hatası bir hata değildir (SFINAE) bir durumu ifade eder C ++ geçersiz bir ikame şablon parametreler kendi başına bir hata değildir. David Vandevoorde, ilgili programlama tekniklerini tanımlamak için ilk olarak SFINAE kısaltmasını tanıttı.[1]

Özellikle, bir aday kümesi oluştururken aşırı yük çözümü, bu kümenin bazı (veya tümü) adayları, karşılık gelen şablon parametreleri için ikame edilmiş (potansiyel olarak çıkarılmış) şablon argümanlarına sahip somutlaştırılmış şablonların sonucu olabilir. Herhangi bir şablon için bir dizi bağımsız değişkenin değiştirilmesi sırasında bir hata oluşursa, derleyici bir derleme hatasıyla durmak yerine aday kümeden potansiyel aşırı yüklemeyi kaldırır, bunun yerine C ++ standardının bu tür bir işlemi vermesi koşuluyla.[2] Bir veya daha fazla aday kalırsa ve aşırı yük çözümü başarılı olursa, çağrı iyi biçimlidir.

Misal

Aşağıdaki örnek, SFINAE'nin temel bir örneğini göstermektedir:

yapı Ölçek {  typedef int foo;};şablon <typename T>geçersiz f(typename T::foo) {}  // Tanım 1şablon <typename T>geçersiz f(T) {}  // Tanım # 2int ana() {  f<Ölçek>(10);  // 1 numaralı telefonu arayın.  f<int>(10);   // 2 numaralı telefonu arayın. Hatasız (int :: foo olmamasına rağmen)                // SFINAE'ye teşekkürler.}

Burada, nitelikli bir adda sınıf dışı bir türü kullanmaya çalışmak (T :: foo) için kesinti başarısızlıkla sonuçlanır f Çünkü int adlı iç içe geçmiş türü yok foo, ancak program iyi biçimlendirilmiştir çünkü aday işlevler kümesinde geçerli bir işlev kalır.

SFINAE başlangıçta, ilgisiz şablon bildirimleri görünür olduğunda (örneğin, bir başlık dosyasının dahil edilmesi yoluyla) kötü biçimlendirilmiş programlar oluşturmaktan kaçınmak için tanıtılmış olsa da, birçok geliştirici daha sonra bu davranışı derleme zamanı iç gözlem için yararlı buldu. Spesifik olarak, bir şablonun örnekleme zamanında şablon argümanlarının belirli özelliklerini belirlemesine izin verir.

Örneğin SFINAE, bir türün belirli bir typedef içerip içermediğini belirlemek için kullanılabilir:

#Dahil etmek <iostream>şablon <typename T>yapı has_typedef_foobar {  // "evet" ve "hayır" tiplerinin farklı boyutlara sahip olması garanti edilir,  // özellikle sizeof (yes) == 1 ve sizeof (hayır) == 2.  typedef kömür Evet[1];  typedef kömür Hayır[2];  şablon <typename C>  statik Evet& Ölçek(typename C::foobar*);  şablon <typename>  statik Hayır& Ölçek(...);  // Test  (nullptr) çağrısının sonucunun "sizeof" değeri şuna eşitse:  // sizeof (yes), ilk aşırı yükleme çalıştı ve T adlı iç içe bir türe sahip  // foobar.  statik sabit bool değer = boyutu(Ölçek<T>(nullptr)) == boyutu(Evet);};yapı foo {  typedef yüzen foobar;};int ana() {  std::cout << std::Boolalpha;  std::cout << has_typedef_foobar<int>::değer << std::son;  // Yanlış yazdırır  std::cout << has_typedef_foobar<foo>::değer << std::son;  // Doğru yazdırır}

Ne zaman T iç içe geçmiş türe sahiptir foobar tanımlanmış, ilkinin somutlaştırılması Ölçek çalışır ve boş gösterici sabiti başarıyla geçilir. (Ve ortaya çıkan ifade türü şu şekildedir: Evet.) Çalışmazsa, mevcut tek işlev ikinci işlevdir. Ölçekve sonuçta ortaya çıkan ifade türü Hayır. Bir üç nokta yalnızca herhangi bir argümanı kabul edeceği için değil, aynı zamanda dönüşüm sıralaması en düşük olduğu için de kullanılır, bu nedenle mümkünse ilk işleve çağrı tercih edilir; bu belirsizliği ortadan kaldırır.

C ++ 11 basitleştirme

İçinde C ++ 11, yukarıdaki kod şu şekilde basitleştirilebilir:

#Dahil etmek <iostream>#Dahil etmek <type_traits>şablon <typename... Ts>kullanma void_t = geçersiz;şablon <typename T, typename = geçersiz>yapı has_typedef_foobar : std::false_type {};şablon <typename T>yapı has_typedef_foobar<T, void_t<typename T::foobar>> : std::true_type {};yapı foo {  kullanma foobar = yüzen;};int ana() {  std::cout << std::Boolalpha;  std::cout << has_typedef_foobar<int>::değer << std::son;  std::cout << has_typedef_foobar<foo>::değer << std::son;}

Algılama deyiminin standardizasyonu ile Kütüphane temel v2 (n4562) teklif, yukarıdaki kod aşağıdaki gibi yeniden yazılabilir:

#Dahil etmek <iostream>#Dahil etmek <type_traits>şablon <typename T>kullanma has_typedef_foobar_t = typename T::foobar;yapı foo {  kullanma foobar = yüzen;};int ana() {  std::cout << std::Boolalpha;  std::cout << std::Tespit edildi<has_typedef_foobar_t, int>::değer << std::son;  std::cout << std::Tespit edildi<has_typedef_foobar_t, foo>::değer << std::son;}

Geliştiricileri Boost SFINAE'yi güçlendirmede kullandı :: enable_if[3] ve başka şekillerde.

Referanslar

  1. ^ Vandevoorde, David; Nicolai M. Josuttis (2002). C ++ Şablonları: Tam Kılavuz. Addison-Wesley Profesyonel. ISBN  0-201-73484-2.
  2. ^ Uluslararası Standardizasyon Örgütü. "ISO / IEC 14882: 2003, Programlama dilleri - C ++", § 14.8.2.
  3. ^ Artırma Eğer