2012-07-04 42 views
8

uzman şablon sınıfını almak değildir: "özel" uzman functor ve baskı almaya Aşağıdaki koddaSFINAE: Derleyici Bir <a href="http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error" rel="noreferrer">SFINAE</a> sorun var

, ben C++ istediğiniz derleyici, ancak genel" baskı oluyor " yerine.

#include <iostream> 
#include <vector> 

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

template<class T> 
struct Functor<T, typename T::Vec> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

Özel yapılandırmanın otomatik olarak kullanılmasını sağlamak için bunu nasıl düzeltebilirim? Not Foo üzerinde Functor yapısını doğrudan uzmanlaştırmak istemiyorum, ancak Vec türüne sahip tüm türlerde uzmanlaşmak istiyorum. .: Geçen cevap sizi yanıltıcı için 4.4.4

+0

'derleyici 'etiketini kaldırıldı, genellikle derleme işleminin kendisi hakkında soru sormak için kullanılır, oysa bu soru C++ dili hakkındadır. –

cevap

11

Maalesef ++ g kullanıyorum

P.S., ben daha basit olurdu bir süre düşündü. Bu yüzden burada eksiksiz bir çözüm sunmaya çalışacağım.

taşıyıcılığının

: Genel yaklaşım sorunların bu tür bir özellikleri yardımcı şablonu yazmak ve bir sınıf ihtisas karar vermek (C++ 11, boost veya manuel uygulama ya) enable_if ile birlikte kullanmaktır çözmek için ,

template <typename T> 
struct has_nested_Vec { 
    typedef char yes; 
    typedef char (&no)[2]; 
    template <typename U> 
    static yes test(typename U::Vec* p); 
    template <typename U> 
    static no test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

yaklaşım basittir iki şablon işlevleri, farklı boyutlarda olduğu dönüş türlerini sağlar: yazmaya

basit bir yaklaşım, mutlaka iyi ama basit olurdu. Bunlardan biri yuvalanmış Vec tipini ve diğerini elips alır. İç içe geçmiş Vec olan tüm bu türler için, ilk aşırı yük daha iyi eşleşir (elips her tür için en kötü eştir). İç içe geçmiş Vec SFINAE içermeyen tipler için bu aşırı yük atılır ve tek seçenek elips sayılır. Yani şimdi herhangi bir türün iç içe geçmiş Vec türüne sahip olup olmadığını sormak için bir özellik var.

herhangi kitaplıktan kullanabilirsiniz eğer

etkinleştirme, veya kendi dönebilirsiniz, oldukça basittir:

template <bool state, typename T = void> 
struct enable_if {}; 

template <typename T> 
struct enable_if<true,T> { 
    typedef T type; 
}; 

İlk argüman false olduğunu baz şablon Tek seçenek ve iç içe type, koşul true ise, enable_if, SFINAE ile kullanabileceğimiz bir iç içe type sahiptir.

Uygulama

Şimdi şablonu hem de iç içe Vec ile sadece bu tipleri için SFINAE kullanacak ihtisas sağlamanız gerekir: Biz bir tip Functor örneğini zaman

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
     std::cerr << "general" << std::endl; 
    } 
}; 
template<class T> 
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > { 
    void operator()() const { 
     std::cerr << "special" << std::endl; 
    } 
}; 

, derleyici, has_nested_Vec örneğini başlatacak ve enable_if'a geçirilen bir gerçek değer elde edecek olan uzmanlığı kullanmaya çalışacaktır. Değer false olan enable_if iç içe type türünde olmayan türler için, bu nedenle SFINAE'de uzmanlık atılacak ve temel şablon kullanılacak.

Sizin özel bir durum gerçekten sen misin yok, tek birine üç unsuru karıştırabilirsiniz bütün tip ama sadece operatörü uzmanlaşmak gerekir görünüyor belirli durumda,

: Bir Bu eski bir soru olsa da

template <typename T> 
class Functor { 
    template <typename U> 
    void op_impl(typename U::Vec* p) const { 
     std::cout << "specialized"; 
    } 
    template <typename U> 
    void op_impl(...) const { 
     std::cout << "general"; 
    } 
public: 
    void operator()() const { 
     op_impl<T>(0); 
    } 
}; 
2

, ben hala bir çift sağlayarak değer olduğunu düşünüyorum: enable_if ihtiyacını ve özellikleri sınıfı kaldırarak, Vec varlığına dayalı iki dahili templated işlevlerinden birine gönderir Functor Orijinal kodu hızla düzeltmek için daha fazla alternatif.

Temel sorun SFINAE (bu bölümü, aslında, gayet) kullanımı ile değil, ancak kısmi uzmanlık verilen değişken birincil şablonu (void) varsayılan parametresinin eşleme (typename T::Vec) ile . Birincil şablondaki varsayılan parametre nedeniyle, Functor<Foo> aslında Functor<Foo, void> anlamına gelir. Derleyici, uzmanlığı kullanarak bunu başlatmaya çalıştığında, iki argümanı uzmanlıktakilerle eşleştirmeye çalışır ve void, std::vector<int> için değiştirilemez. Daha sonra, birincil şablon kullanılarak örneklemeye geri düşer.

Yani, tüm Vec s std::vector<int> s varsayar hızlı düzeltme,, uzmanlaşma şimdi kullanılacak bu

template<class T, class E = std::vector<int>> 

doğrultusunda

template<class T, class V = void> 

değiştirmektir çünkü argümanlar eşleşecektir. Basit ama çok sınırlayıcı. Açıkçası, ana şablonda varsayılan parametre olarak belirleyebileceğimiz bir şeyle eşleşmesi için uzmanlıktaki argümanın türünü daha iyi kontrol etmeliyiz. Yeni özellikleri tanımlayan gerektirmez biri hızlı çözüm şudur:

#include <iostream> 
#include <vector> 
#include <type_traits> 

template<class T, class E = std::true_type> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

template<class T> 
struct Functor<T, typename std::is_reference<typename T::Vec&>::type> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

Bu temel türleri ve diziler, örneğin, ve referanslar veya bunlara işaretçiler dahil burada mantıklı olabilecek herhangi Vec türü için çalışacaktır.

1

Bir üye türünün varlığını algılamak için başka bir alternatif de void_t kullanmaktır. Geçerli kısmi uzmanlıklar, varsayılan parametre (ler) ile eşleştiği sürece genel uygulamaya tercih edilebilir olduğundan, geçerli olduğunda void olarak değerlendirilen bir tür istiyoruz ve yalnızca belirtilen üye olduğunda geçerlidir; Bu tür, yaygın olarak (ve C++ 17'den itibaren, kanonik olarak) void_t olarak bilinir. senin derleyici düzgün desteklemiyorsa

template<class...> 
using void_t = void; 

(erken C++ 14 derleyiciler, takma şablonları kullanılmayan parametreler yukarıda void_t kırarak, SFINAE sağlamak için garanti değildi), bir geçici çözüm mevcuttur. C++ 17 de

template<typename... Ts> struct make_void { typedef void type; }; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

, void_t type_traits içinde, yardımcı kütüphanesi kullanılabilir. istendiği gibi, bu birlikte

#include <iostream> 
#include <vector> 
#include <type_traits> // For void_t. 

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

// Use void_t here. 
template<class T> 
struct Functor<T, std::void_t<typename T::Vec>> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

, çıktı, special olup.


Bu durumda, üye türünün varlığını kontrol ettiğimizden, işlem çok basittir; SFINAE veya type_traits kütüphanesi olmadan yapılabilir, gerekirse C++ 03 tesislerini kullanmamıza izin verir. Bildiğim kadarıyla

// void_t: 
// Place above Functor's definition. 
template<typename T> struct void_t { typedef void type; }; 

// ... 

template<class T> 
struct Functor<T, typename void_t<typename T::Vec>::type> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

, bu, çoğu üzerinde hepsi değilse, SFINAE özellikli C++ 03-, C++ 11-, C++ 14- veya C++ 1z uyumlu derleyiciler çalışması gerekir . Bu, standardın biraz gerisinde kalan derleyicilerle ya da henüz C++ 11 uyumlu derleyicileri olmayan platformlar için derleme yaparken yararlı olabilir.


void_t hakkında daha fazla bilgi için cppreference bakın.

İlgili konular