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

İkame hatası bir hata değildir ( SFINAE ) , şablon parametrelerinin geçersiz bir şekilde değiştirilmesinin kendi başına bir hata olmadığı C ++ 'da bir durumu ifade eder . David Vandevoorde, ilgili programlama tekniklerini tanımlamak için ilk olarak SFINAE kısaltmasını tanıttı.

Spesifik olarak, aşırı yük çözümü için bir aday küme yaratılırken , 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ı ile başlatı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. 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:

struct Test {
  typedef int foo;
};

template <typename T>
void f(typename T::foo) {}  // Definition #1

template <typename T>
void f(T) {}  // Definition #2

int main() {
  f<Test>(10);  // Call #1.
  f<int>(10);   // Call #2. Without error (even though there is no int::foo)
                // thanks to SFINAE.
}

Burada, uzman bir isim (non-sınıf türünü kullanmaya çalışırken T::foobir kesinti başarısızlıkla) sonuçları f<int>nedeniyle intadında iç içe türü vardır foo, ancak program gerektiği gibi biçimlendirilmiş aday fonksiyonlarının kümesi içinde geçerli fonksiyon kalıntıları yüzünden.

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:

#include <iostream>

template <typename T>
struct has_typedef_foobar {
  // Types "yes" and "no" are guaranteed to have different sizes,
  // specifically sizeof(yes) == 1 and sizeof(no) == 2.
  typedef char yes[1];
  typedef char no[2];

  template <typename C>
  static yes& test(typename C::foobar*);

  template <typename>
  static no& test(...);

  // If the "sizeof" of the result of calling test<T>(nullptr) is equal to
  // sizeof(yes), the first overload worked and T has a nested type named
  // foobar.
  static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};

struct foo {
  typedef float foobar;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << has_typedef_foobar<int>::value << std::endl;  // Prints false
  std::cout << has_typedef_foobar<foo>::value << std::endl;  // Prints true
}

Ne zaman Tiç içe türü vardır foobartanımlanan, ilk örneğinin testeserleri ve boş işaretçi sabit başarıyla geçirilir. (Ve sonuçta ortaya çıkan ifade türü yes.) Çalışmazsa, mevcut tek işlev ikincidir testve sonuçta ortaya çıkan ifade türü no. Üç 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

In C ++ 11 , yukarıdaki kod basitleştirilmiş olabilir:

#include <iostream>
#include <type_traits>

template <typename... Ts>
using void_t = void;

template <typename T, typename = void>
struct has_typedef_foobar : std::false_type {};

template <typename T>
struct has_typedef_foobar<T, void_t<typename T::foobar>> : std::true_type {};

struct foo {
  using foobar = float;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << has_typedef_foobar<int>::value << std::endl;
  std::cout << has_typedef_foobar<foo>::value << std::endl;
}

Kütüphane temel v2 (n4562) teklifindeki algılama deyiminin standartlaştırılmasıyla , yukarıdaki kod aşağıdaki gibi yeniden yazılabilir:

#include <iostream>
#include <type_traits>

template <typename T>
using has_typedef_foobar_t = typename T::foobar;

struct foo {
  using foobar = float;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << std::is_detected<has_typedef_foobar_t, int>::value << std::endl;
  std::cout << std::is_detected<has_typedef_foobar_t, foo>::value << std::endl;
}

Boost geliştiricileri, SFINAE'yi boost :: enable_if ve başka şekillerde kullandılar.

Referanslar