Floating point concept für templates



  • Ich hatte mal irgendwo ein nettes Makro für C++11 gesehen:

    #include <type_traits>
    
    #define REQUIRES(...) ,class=typename std::enable_if<(__VA_ARGS__)>::type
    
    template<class T
      REQUIRES( std::is_floating_point<T>::value )
    >
    void foo(T x);
    

    Das funzt, weil in C++11 Funktionstemplates Default-Template-Parameter haben dürfen. Allerdings klappt das nicht mehr mit Variadischen Templates, glaube ich.

    Es wäre ja auch nur praktisch gewesen, wenn C++11 so etwas direkt unterstützt hätte *seufz*. Ich bin mal gespannt, (ob) was aus dem Concept-C++-Lager noch kommen wird.



  • Werner Salomon schrieb:

    berndbernd schrieb:

    Danke euch allen. Habs jetzt so gelöst:

    BOOST_STATIC_ASSERT(boost::is_floating_point<T>::value);
    

    Hallo Bernd,

    der Nachteil von einem STATIC_ASSERT ist, dass im Falle eines Compilefehlers, dieser nur den Ort des STATIC_ASSERT angibt und nicht den Ort wo z.B. die Funktin mit dem unerwünschten Typen aufgerufen wird. Besser ist deshalb der Einsatz von boost.enable_if. In Deinem Fall etwa so:

    #include <boost/type_traits/is_floating_point.hpp>
    #include <boost/utility/enable_if.hpp>
    
    template<class T>
    typename boost::enable_if< boost::is_floating_point< T >, void >::type // Bem.: void kann man auch weglassen, da Defaulttyp
        foo(T arg)
    {
        // don't compile if T neither float nor double
    }
    
    int main()
    {
        using namespace std;
        foo( 3. ); // ok
        foo( 3 ); // liefert 'Failed to specialize function template' mit Fehler in DIESER Zeile
        return 0;
    }
    

    Gruß
    Werner

    Das ist natürlich ein gutes Argument. Gibt es in ähnlicher Weise auch eine Möglichkeit, das Concept auf eine Klasse und nicht nur eine Funktion zu beziehen?

    Grüße



  • berndbernd schrieb:

    Gibt es in ähnlicher Weise auch eine Möglichkeit, das Concept auf eine Klasse und nicht nur eine Funktion zu beziehen?

    http://www.boost.org/doc/libs/1_47_0/libs/utility/enable_if.html#htoc6



  • berndbernd schrieb:

    Das ist natürlich ein gutes Argument. Gibt es in ähnlicher Weise auch eine Möglichkeit, das Concept auf eine Klasse und nicht nur eine Funktion zu beziehen?

    Ja ..

    Werner Salomon schrieb:

    Besser ist deshalb der Einsatz von boost.enable_if.

    folge dem Link 😉 und lese das Kapitel '3.1 Enabling template class specializations'


  • Mod

    krümelkacker schrieb:

    Ich hatte mal irgendwo ein nettes Makro für C++11 gesehen:

    #include <type_traits>
    
    #define REQUIRES(...) ,class=typename std::enable_if<(__VA_ARGS__)>::type
    
    template<class T
      REQUIRES( std::is_floating_point<T>::value )
    >
    void foo(T x);
    

    Das funzt, weil in C++11 Funktionstemplates Default-Template-Parameter haben dürfen. Allerdings klappt das nicht mehr mit Variadischen Templates, glaube ich.

    Sehr interessant. Damit könnte man enable_if auch für Konvertierungsoperatoren benutzen. Bei einer (oberflächlichen) Suche nach Regeln, die dieses Konstrukt im Zusammenhang mit Funktionsparameterpacks verbieten, habe ich erst einmal nichts gefunden.



  • Werner Salomon schrieb:

    folge dem Link 😉 und lese das Kapitel '3.1 Enabling template class specializations'

    Ah, da war ich schon mal 🙂 Habs irgendwie beim ersten Mal nicht geblickt.

    Sehe ich das richtig, dass ich auf alle Fälle eine Standard-Template-Deklaration brauche und dann halt die Spezialisierung mit dem enable_if für Floatingpoint-Typen - selbst wenn es die Klasse ausschließlich für Floatingpoint-Typen geben soll?

    Grüße



  • Werner Salomon schrieb:

    der Nachteil von einem STATIC_ASSERT ist, dass im Falle eines Compilefehlers, dieser nur den Ort des STATIC_ASSERT angibt und nicht den Ort wo z.B. die Funktin mit dem unerwünschten Typen aufgerufen wird. Besser ist deshalb (...)

    Ach, das kommt auf den Compiler drauf an:

    http://ideone.com/6aiYZ



  • Bei Templates ist es doch üblich, dass die Instanziierungshierarchie aufgelistet wird, daher sind entsprechende Fehlermeldungen ja auch oft so lang.

    An enable_if stört mich, dass die Schnittstelle unübersichtlich wird (ist z.B. für Doxygen problematisch). Das REQUIRES -Makro sieht jedoch interessant aus...



  • Nexus schrieb:

    An enable_if stört mich, dass die Schnittstelle unübersichtlich wird

    Ja, mich auch.

    Wenn man nur ne Fehlermeldung will finde ich static_assert() gut. Kommt IMO einfach drauf an was man will/braucht.
    Also ob es sofort ne Fehlermeldung geben soll, oder andere Overloads berücksichtigt werden sollen (SFINAE halt).

    Und so lange ich mir nicht sicher bin was die bessere Variante ist, tendiere ich zu static_assert() , da man damit schneller ne Fehlermeldung bekommt. Und das ist IMO immer gut, wenn man was macht, was nicht vorgesehen war (und es kann nicht gut vorgesehen gewesen sein, wenn man sich nicht sicher war ob man SFINAE braucht oder nicht).

    BTW: auch MSVC 10 gibt mit static_assert() ne brauchbare Fehlermeldung aus:

    1>vc10test.cpp(7): error C2338: will floating point!!!11elf
    1>          vc10test.cpp(13) : see reference to function template instantiation 'void foo<int>(T)' being compiled
    1>          with
    1>          [
    1>              T=int
    1>          ]
    


  • berndbernd schrieb:

    Sehe ich das richtig, dass ich auf alle Fälle eine Standard-Template-Deklaration brauche und dann halt die Spezialisierung mit dem enable_if für Floatingpoint-Typen - selbst wenn es die Klasse ausschließlich für Floatingpoint-Typen geben soll?

    So ist es in der Hilfe beschrieben. Wobei ich nicht ausschließen will, dass es noch andere Lösungen gibt.
    In Deinem Fall sähe es wohl so aus:

    #include <boost/type_traits/is_floating_point.hpp>
    #include <boost/utility/enable_if.hpp> 
    
    template< typename T, typename Enable = void >
    struct Foo; // undefined struct
    
    template< typename T >
    struct Foo< T, typename boost::enable_if< boost::is_floating_point< T > >::type >
    {
        // ... hier kommt die Implementierung von Foo<T>
    };
    
    int main()
    {
        Foo< double > d;
        Foo< int > i; // <== Compilefehler: 'i' uses undefined struct 'Foo<T>'
        return 0;
    }
    

    Gruß
    Werner


Anmelden zum Antworten