SFINAE/has_mem_fun: Templates unterstützen



  • Hallo!
    Ich habe für meine Library ein Template gebaut, welches überprüft, ob eine bestimmte Klasse (oder eine Basisklasse davon) eine Memberfunktion mit einer ganz bestimmten Signatur besitzt:

    #include <iostream>
    #include <type_traits>
    
    struct any final { };
    
    // die erste Spezialisierung für den mutable-Fall, die zweite für konstante Memberfunktionen - geht das eleganter?
    #define GENERATE_HAS_MEM_FUN(fun)\
    template <typename T, typename...>\
    class has_mem_fun_##fun {\
    	static_assert(std::integral_constant<T, false>::value, "has_mem_fun_##fun needs function type as second template parameter");\
    };\
    template <typename T, typename ReturnType, typename ...Args, typename ...TemplateArgs>\
    class has_mem_fun_##fun<ReturnType(T::*)(Args...), TemplateArgs...> {\
    	template <typename U> static std::true_type test(ReturnType(U::*)(Args...));\
    	template <typename U> static decltype(test(&U::template fun<TemplateArgs...>)) test(decltype(&U::template fun<TemplateArgs...>), int);\
    	template <typename U> static decltype(test(&U::fun)) test(decltype(&U::fun), int);\
    	template <typename U> static std::false_type test(...);\
    public:\
    	static constexpr bool value = decltype(test<T>(nullptr, 0))::value;\
    };\
    template <typename T, typename ReturnType, typename ...Args, typename ...TemplateArgs>\
    class has_mem_fun_##fun<ReturnType(T::*)(Args...) const, TemplateArgs...> {\
    	template <typename U> static std::true_type test(ReturnType(U::*)(Args...) const);\
    	template <typename U> static decltype(test(&U::fun)) test(decltype(&U::fun), int);\
    	template <typename U> static decltype(test(&U::template fun<TemplateArgs...>)) test(decltype(&U::template fun<TemplateArgs...>), int);\
    	template <typename U> static std::false_type test(...);\
    public:\
    	static constexpr bool value = decltype(test<T>(nullptr, 0))::value;\
    };
    
    struct foo {
       int bar(double*) const;
       template <typename T>
       void foobar(int, T&);
    };
    
    GENERATE_HAS_MEM_FUN(bar);
    GENERATE_HAS_MEM_FUN(foobar);
    
    int main() {
       // Funktioniert bestens in VS 2017, GCC und Clang
       std::cout << std::boolalpha << has_mem_fun_bar<int (foo::*)(double*) const>::value
       // Funktioniert nicht in VS (Template-Version):
                 << has_mem_fun_foobar<void (foo::*)(int, any&), any>::value;
    }
    

    VS sagt dazu:

    error C2535: 'unknown-type has_mem_fun_bar<ReturnType(__cdecl T::* )(Args...),TemplateArgs...>::test(unknown-type,int)': member function already defined or declared
    

    ... was wohl so viel heißt wie, dass es den Overload der test -Funktion nicht rafft und die ODR verletzt sieht.
    Hat jemand eine Idee für einen Work-Around, wie ich das für VS irgendwie hinbekomme?


  • Mod

    Ich habe für meine Library ein Template gebaut, welches überprüft, ob eine bestimmte Klasse (oder eine Basisklasse davon) eine Memberfunktion mit einer ganz bestimmten Signatur besitzt:

    Was soll das bringen, vor allem dieser Quatsch mit Membertemplates? Jeder Einsatz eines solchen Templates geht schon gegen die Natur von C++ Metaprogrammierung: Wir sollen nur Eigenschaften von Typen annehmen, denn Reflektion ist (leider noch) kein Bestandteil der Sprache, und sie zu simulieren wird scheitern.

    Du kannst, um das Redefinitions-Problem zu lösen, die Strategie in meiner Antwort zu VS und SFINAE verwenden.

    // die erste Spezialisierung für den mutable-Fall, die zweite für konstante Memberfunktionen - geht das eleganter?
    

    Das ist sowieso ungenügend. Siehe meine Antwort hier: http://stackoverflow.com/questions/27743745/stripping-all-qualifiers-from-a-function-type
    Du hast variadische Funktionen und solche mit ref-qualifiers vergessen, und bald kommt transaction_safe dazu.



  • Danke, der Workaround funktioniert.

    Dass das ohne Sprachunterstützung nicht perfekt oder sehr umständlich ist, ist mir durchaus klar, aber das ist kein Grund, nicht trotzdem eine Approximation nutzen zu können. Leider ist noexcept nicht Teil der Signatur, wodurch ich auf diese Weise noexcept nicht erzwingen kann (genau so wie virtual). Ohne Sprachunterstützung (i.e. irgendeine Form von Concepts) werde ich darauf wohl verzichten müssen.

    Was ist an Memberfunktionstemplates Quatsch?

    Ref-Funktionen hab ich nicht vergessen, deswegen frage ich ja nach einer besseren Lösung, weil ich dann mit const schon 222\cdot2 Spezialisierungen benötige.

    Variadische Template-Member habe ich bisher noch nicht gebraucht. Dafür eine Lösung zu finden klingt aber spannend, darüber mache ich mir mal beizeiten Gedanken.



  • Ich hab auch ein wenig Metacpluspluselgefusel in der Freizeit gemacht.

    Ich habe dafür auch mal so was gemacht, was nicht viele Zeilen vollbracht hat und auch nicht viel getestet hab. Und mit gängigen Methoden funktioniert hat.

    Aber eigentlich nie wirklich gebraucht, soweit, soweit.

    Find ich aber toll, dass man manchmal solche Zeilen hier noch zu Auge kriegt. Kann man sich doch noch glücklich schätzen, wenn man ein Deutscher ist *sich müh mach*!


Log in to reply