partielle Spezialisierung mit einer inneren Klasse



  • Hallo Gurus,

    ich habe ein Klassen-Template welches im Prinzip so aussieht:

    template< typename T >
    struct Pups
    {
        struct Inner
        {
            T* ptr_;
        };
    
        T egal_;
    };
    

    Weiter gibt es eine 'Eigenschaft':

    template< typename T >
    struct is_pups : public std::false_type {};
    

    .. so dass man z.B. Pups<> diese Eigenschaft geben könnte:

    template< typename T >
    struct is_pups< Pups< T > > : public std::true_type {};
    

    das funktioniert. Leider geht dies (natürlich!?) nicht mit der Klasse Inner - also:

    template< typename T > // error C2764: 'T' : template parameter not deducible in partial specialization!
    struct is_pups< typename Pups< T >::Inner > : public std::true_type {};
    

    hat irgendjemand eine Idee wie man das trotzdem hin bekommt, ohne aus Pups<>::Inner eine eigenständige Klasse zu machen - versteht sich?

    Gruß
    Werner


  • Mod

    Man kann (AFAIK) nicht unmittelbar pruefen ob ein Typ Member einer Spezialisierung eines bestimmten Templates ist. Die einzelnen Inner s sind demnach, grob gesagt, voellig unabhaengige Typen.

    Du muesstest ihnen eine gemeinsame pruefbare Eigenschaft geben, bspw. eine gemeinsame, leere, geheime Basisklasse. Das Primaertemplate schreibst du dann entsprechend um.

    Also ganz grob so:

    #include <type_traits>
    
    class Schatz
    {
    	class Geheim {};
    	template<typename> friend struct Pups;
    	template<typename> friend struct is_pups;
    };
    
    template< typename T >
    struct Pups
    {
        struct Inner : Schatz::Geheim
        {
            T* ptr_;
        };
        // ..
    };
    
    template< typename T >
    struct is_pups : public std::is_base_of<Schatz::Geheim, T> {};
    
    static_assert( is_pups<Pups<float>::Inner>::value, "" );
    
    int main() {}
    


  • Mach in inner ein Tapeten outer und dann ändere die nicht spezialisierte zu:

    template <typename T>
    struct is_pups : is_pups<typename T::outer> {};
    

    Und dann einen enable if für alle ohne outer.


  • Mod

    @nathan:

    struct Hacker
    {
        using outer = Pups<float>;
    };
    

    Wenn das egal ist, ist deine Loesung natuerlich vorzuziehen.


  • Mod

    Eine Möglichkeit, die ohne Modifikation der inneren Klasse auskommt, besteht in der geschickten Nutzung von ADL

    #include <utility>
    
    using no = char[1];
    using yes = char[2];
    
    template <typename T>
    struct foo
    {
        template <typename U>
        friend typename std::enable_if<!std::is_same<foo,U>::value, yes&>::type is_nested_fun(U);
    
        struct bar {};
    };
    
    no& is_nested_fun(...);
    template <typename T>
    struct is_foo_nested
        : std::integral_constant<bool, (sizeof(is_nested_fun(std::declval<T>()))==sizeof(yes))> {};
    
    static_assert( !is_foo_nested<int>::value, "" );
    static_assert( !is_foo_nested<foo<int>>::value, "" );
    static_assert( is_foo_nested<foo<int>::bar>::value, "" );
    


  • Ja, das stimmt.
    Aber hacken kann man auch einfach durch Spezialisierung.
    Vollständig sähe meine Lösung übrigens so aus: http://ideone.com/iBSXpf
    Ich sollte echt nicht mehr versuchen, auf dem Handy Beiträge zu schreiben.



  • Oha!

    ein Dankeschön an Euch drei, aber die Lösung von camper muss ich erst mal verdauen. Wenn ich sie nicht verstehe, so melde ich mich noch einmal 😉

    Gruß
    Werner


  • Mod

    Werner Salomon schrieb:

    Oha!

    ein Dankeschön an Euch drei, aber die Lösung von camper muss ich erst mal verdauen. Wenn ich sie nicht verstehe, so melde ich mich noch einmal 😉

    Gruß
    Werner

    Meine Lösung ist defekt und nicht zu empfehlen: wenn foo<T> als Templateargument dient, ergibt sich für die entsprechende Templateklasse ein falsches Ergebnis.

    static_assert( !is_foo_nested<foo<foo<int>>>::value, "geht leider schief" );
    

Log in to reply