Şablon uzmanlığı ve kalıtım ile ilgili iyi uygulamalar

Şablon uzmanlığı kalıtım hiyerarşisini dikkate almaz. Örneğin, Base için bir şablonu uzmanlaştırırsam ve Türetilmiş ile başlatabilirsem, uzmanlık seçilemez (aşağıdaki kod (1) 'e bakınız).

Bu büyük bir engel olabilir, çünkü bazen Liskov ikame prensibinin ihlaline yol açar. Örneğin, bu soruda çalışırken, std ile Boost.Range algoritmalarını kullanamadığımı fark ettim: : std :: pair ile yapabildiğimde sub_match . sub_match , pair öğesinden herkese açık olarak yayınlandığından, sağduyu sub_match yerine her bir çifti kullanıldığını belirtebilirdi Ancak bu, şablon uzmanlığı kullanan özellik sınıflarından dolayı başarısız oluyor.

Bu sorunu, enable_if ve is_base_of ile birlikte kısmi şablon uzmanlığı kullanarak çözebiliriz (bkz. Kod (2)). Bu çözümü, özellikle de kütüphane kodu yazarken, tam uzmanlaşmadan yana mı kullanmalıyım? Bu yaklaşıma nezaret ettiğim herhangi bir sakınca var mı? Sık kullandığınız veya kullanmış olduğunuz bir uygulama mı?


Örnek kodları

(1)
#include 

struct Base {};
struct Derived : public Base {};

template < typename T >
struct Foo
{
    static void f() { std::cout << "Default" << std::endl; }
};

template <>
struct Foo< Base >
{
    static void f() { std::cout << "Base" << std::endl; }
};

int main()
{
    Foo::f();//prints "Default"
}

(2)
#include 
#include 

struct Base {};
struct Derived : public Base {};

template 
struct Foo
{
    static void f() { std::cout << "Default" << std::endl; }
};

template 
struct Foo<
    T, typename 
    std::enable_if< std::is_base_of< Base, T >::value >::type
>
{
    static void f() { std::cout << "Base" << std::endl; }
};

int main()
{
    Foo::f();//prints "Base"
}
23
+1. Gerçekten de mükemmel bir soru.
katma yazar Nawaz, kaynak
+1 mükemmel soru ve motivasyon.
katma yazar sehe, kaynak

1 cevap

enable_if is more flexible

Bence enable_if yaklaşımını tercih etmelisiniz: ihtiyaç duyabileceğiniz her şeyi ve daha fazlasını mümkün kılar.

Örneğin. Bir Türetilmiş sınıfın bir Taban için Liskov-Subsitutable, ama [aynı şeyi/başvuruyu kabul edemezsiniz] aynı özelliklerin/uzmanlıkların geçerli olması (örneğin, Temel POD sınıfı olduğu için) olabilir. Bununla birlikte, türetilmiş ve POD olmayan davranış ya da sınıf bileşimine tamamen ortogonal olan bir şey gibi.

enable_if gives you the power to define exactly the conditions.

Hibrit yaklaşım

Ayrıca, genel amaçlı özelliklerden bazı uygulamaya özel özellikler türeten bir özellik sınıfı uygulayarak da bir takım ortalamayı başarabilirsiniz. 'Özel' özellikler, özellikleri istediğiniz gibi polimorfik olarak uygulamak için enable_if ve meta programlama tekniklerini kullanabilir. Bu şekilde, gerçek uygulamalarınız bazı karmaşık enable_if/dispatch danslarını tekrarlamak zorunda değildir, bunun yerine sadece özel özellik sınıfını (karmaşıklığı gizleyen) tüketebilir.

I think some (many?) Boost libraries use the Hibrit yaklaşım (I've seen it in some capacity where it bridges e.g. fusion/mpl, I think also various iterator traits in Spirit).

Bu yaklaşımı kişisel olarak benimsedim çünkü 'sıhhi tesisat' bir kütüphanenin çekirdek işinden etkili bir şekilde izole edilebildiğinden, bakım yapmak ve dokümantasyon (!) Çok daha kolay.

13
katma
@sehe: ör. mytraits gibi bir şeyi tercih edersiniz :: eligible_for_this_specialization :: std :: enable_if yerine :: value> :: type?
katma yazar user396672, kaynak
@ user396672: hayır, enable_if'den kaçınmayı tercih ediyorum ve doğrudan mytraits kullanıyorum. Özel özellikler sınıfları içindeki koşulluları (muhtemelen enable_if 'e göre) gizleyebilirsiniz.
katma yazar sehe, kaynak
Kütüphane tasarımcılarının kütüphanenin tüm olası kullanıcıları için bu ön yüze karar veremediklerini düşünüyorum. ( std :: pair ile sınırlama örnekleri örneği gibi sınır durumları olabilir, ancak genel olarak kütüphane tasarımcılarının, örneğin; is-a std :: pair <...> de olmalıdır.
katma yazar sehe, kaynak
Çok iyi cevap, teşekkür ederim. Hayal ettiğiniz hibrit yaklaşımın etiket gönderme ile ilgili olabileceğini düşünüyorum: Bir etiket elde etmek için polimorfik özellik sınıf, daha sonra etiketlere dayalı uzmanlık veya aşırı yük kullanın (etiketler bazen polimorfik olabilir, bu yüzden belki aşırı yükleme için ayrılmalıdır ...).
katma yazar Luc Touraille, kaynak
@ user396672: Tüm bunları düşünmenin bir yolu, temel şablonu özelleştirilebilir kancalar sağlamak için özellik sınıflarını kullanan bir "Şablon Yöntemi" (tasarım deseni) olarak görmektir. Temel şablonun bazı bölümlerini özelleştirmek gerekirse, sadece ilgili özellikleri uzmanlaşmak ("geçersiz kılmak") gerekir. Eğer tüm yapı/davranış değişmişse, o zaman temel şablon hala uzmanlaşabilir ("geçersiz kıl").
katma yazar Luc Touraille, kaynak
Bence iyi bir uygulama, uzmanlığa tabi tutulabilecek herşeyi (muhtemelen polimorfik olarak özelleşmiş olacak) özelliklere dönüştürmek ve kullanıcıları temel şablondan ziyade özellikleri özelleştirme konusunda cesaretlendirmek olurdu (neredeyse bir Şablon Yöntemi gibi). ).
katma yazar Luc Touraille, kaynak
Kullanıcı kolaylığı için, bir kütüphane tarafından sağlanan özellikler özelleştirme muhtemelen polimorfik olmalıdır.
katma yazar Luc Touraille, kaynak
Boost.Range geliştiricilerini suçlamıyorum, std :: pair neyse bundan türetilmiş değildir :).
katma yazar Luc Touraille, kaynak