bestimmte Typen von Template ausschließen



  • Moin zusammen

    Ich möchte aus bestimmten Gründen string-Literale bzw. Pointer im allgemeinen von einem Template als Typen ausschließen.
    D.h. das Funktionstemplate für diese Typen darf gar nicht erst erzeugt werden, weil ich z.b. die StringLiterale in eine gleichnamigen Funktion anders behandeln will.

    Die allgemeine Funktion für die meisten Typen:

        template <typename T, typename std::enable_if <!std::is_pointer<T>::value, T>::type = 0>
        FooBarClass<T> foobar( T value )
        {
            FooBarClass<T> result( std::move( value ) );
            return std::move( result );
        }
    

    Die Funktion für String-Literale:

        FooBarClass<std::string> foobar( const char *value )
        {
            std::string ValueStr( value );
            FooBarClass<std::string> result( std::move( ValueStr ) );
            return std::move( result );
        }
    

    Irgendwas habe ich falsch gemacht, da der compiler die zweite Funktion nicht erzeugen kann, weil sie bereits existiert. D.h. der Compiler hat das Funktionstemplate ebenfalls für "const char*" erzeugt, was ich ja so nicht will.

    Die genaue Fehlermeldung ist:

    multiple definition of `foobar[abi:cxx11](char const*)'|

    Jemand ne Idee?



  • Übersetzbares Beispiel?

    #include <iostream>
    
    template<typename T>
    struct FooBarClass
    {
        T m;
        FooBarClass(T aT) : m(aT) {}
    };
    
    template <typename T, typename std::enable_if <!std::is_pointer<T>::value, T>::type = 0>
    FooBarClass<T> foobar( T value )
    {
        FooBarClass<T> result( std::move( value ) );
        return std::move( result );
    }
    
    FooBarClass<std::string> foobar( const char *value )
    {
        std::string ValueStr( value );
        FooBarClass<std::string> result( std::move( ValueStr ) );
        return std::move( result );
    }
    
    int main()
    {
        auto f = foobar("a");
    }
    
    

    g++ -Wall -Wextra -pedantic -std=c++17 templ.cpp -o templ
    templ.cpp: In Funktion »FooBarClass<std::__cxx11::basic_string<char> > foobar(const char*)«:
    templ.cpp:21:21: Warnung: das Verschieben eines lokalen Objekts in einer return-Anweisung verhindert das Einsparen der Kopie [-Wpessimizing-move]
    21 | return std::move( result );
    | ~^~
    templ.cpp:21:21: Anmerkung: entfernen Sie den Aufruf von »std::move«



  • @It0101 sagte in bestimmte Typen von Template ausschließen:

    Die genaue Fehlermeldung ist:
    multiple definition of `foobar[abi:cxx11](char const*)'|

    Es fehlt einfach inline?



  • Witzigerweise funktioniert das hier, nachdem ich es in ein jungfräuliches Projekt gesteckt habe.

    template <typename T>
    class FoobarClass
    {
    public:
        FoobarClass( T &&val_ ) :
            Value( std::move( val_ ) )
        {
        }
    
    private:
        T Value;
    };
    
    template <typename T, typename std::enable_if <!std::is_pointer<T>::value, T>::type = 0>
    FoobarClass<T> foobar( T value )
    {
        FoobarClass<T> result( std::move( value ) );
        return std::move( result );
    }
    
    FoobarClass<std::string> foobar( const char *value )
    {
        std::string ValueStr( value );
        FoobarClass<std::string> result( std::move( ValueStr ) );
        return std::move( result );
    }
    
    int main()
    {
        auto dummy = foobar( "literal" );
        auto dummy2 = foobar( uint32_t( 17 ) );
        return 0;
    }
    
    

    Der einzige Unterschied ist, dass es jetzt in einer cpp liegt und vorher in einem Header....



  • @It0101 sagte in bestimmte Typen von Template ausschließen:

    Der einzige Unterschied ist, dass es jetzt in einer cpp liegt und vorher in einem Header....

    @manni66 sagte in bestimmte Typen von Template ausschließen:

    @It0101 sagte in bestimmte Typen von Template ausschließen:

    Die genaue Fehlermeldung ist:
    multiple definition of `foobar[abi:cxx11](char const*)'|

    Es fehlt einfach inline?



  • Danke dir 🙂 daran lag es. Und ich Holzkopf dachte, ich hätte die std::enable_if - Geschichte vermurkst 😃



  • komischerweise bekommt er die Spezialisierung für "double" nicht hin....

    
    #include <string>
    
    template <typename T>
    class FoobarClass
    {
    public:
        FoobarClass( T &&val_ ) :
            Value( std::move( val_ ) )
        {
        }
    
    private:
        T Value;
    };
    
    template <typename T, typename std::enable_if <!std::is_pointer<T>::value, T>::type = 0>
    FoobarClass<T> foobar( T value )
    {
        FoobarClass<T> result( std::move( value ) );
        return std::move( result );
    }
    
    int main()
    {
        auto dummy = foobar( double( 17 ) );
        return 0;
    }
    

    'std::enable_if<true, double>::type {aka double}' is not a valid type for a template non-type parameter|



  • std::enable_if ist mir viel zu kompliziert:

    #include <string>
    
    template <typename T>
    class FoobarClass
    {
    public:
        FoobarClass( T &&val_ ) :
            Value( std::move( val_ ) )
        {
        }
    
    private:
        T Value;
    };
    
    template <typename T>
    FoobarClass<T> foobar( T value )
    {
        if constexpr( !std::is_pointer_v<T> ) {
          FoobarClass<T> result( std::move( value ) );
          return std::move( result );
       }
    }
    
    int main()
    {
        auto dummy = foobar( double( 17 ) );
        return 0;
    }
    

    g++ -Wall -Wextra -pedantic -std=c++17 templ.cpp -o templ
    templ.cpp: In Funktion »int main()«:
    templ.cpp:27:10: Warnung: Variable »dummy« gesetzt, aber nicht verwendet [-Wunused-but-set-variable]
    27 | auto dummy = foobar( double( 17 ) );
    | ^~~~~
    templ.cpp: In Instanziierung von »FoobarClass<T> foobar(T) [mit T = double]«:
    templ.cpp:27:39: von hier erfordert
    templ.cpp:21:32: Warnung: das Verschieben eines lokalen Objekts in einer return-Anweisung verhindert das Einsparen der Kopie [-Wpessimizing-move]
    21 | return std::move( result );
    | ^
    templ.cpp:21:32: Anmerkung: entfernen Sie den Aufruf von »std::move«

    Beachte die move Warnung!



  • Was die Warnung angeht: Ich weiß. Ich bin nur immer misstrauisch, weil kein Schwein RVO garantieren kann, oder seh ich das falsch?

    Deine Variante funzt.
    Mich wundert nur, dass er beim Aufruf mit "const char*" z.B. die korrekt "alternative" Funktion verwendet und nicht das template. Denn die Template-Funktion wird ja durchaus für const char* erzeugt, nur sie hat eben keinen Inhalt, oder habe ich da was falsch verstanden?



  • @It0101 sagte in bestimmte Typen von Template ausschließen:

    Was die Warnung angeht. Ich weiß. Ich bin nur immer misstrauisch, weil kein Schwein RVO garantieren kann, oder seh ich das falsch?

    RVO ist garantiert, NRVO (noch) nicht, aber von alles Compilern implementiert.



  • Guter Hinweis. In dem Fall vertraue ich dann doch lieber der RVO 😃



  • @It0101 sagte in bestimmte Typen von Template ausschließen:

    Denn die Template-Funktion wird ja durchaus für const char* erzeugt, nur sie hat eben keinen Inhalt, oder habe ich da was falsch verstanden?

    Ja, es wird aber die besser passende Funktion verwendet.

    Wenn du die Funktion tatsächlich nur auf passende Typen beschränken willst/musst, helfen Concepts:

    template <typename T>
    requires !std::is_pointer_v<T>
    FoobarClass<T> foobar( T value )
    {
          FoobarClass<T> result( std::move( value ) );
          return result;
    }
    

    Alternativ, wenn nicht verfügbar, sollt es auch

    template<class T> struct dependent_false : std::false_type {};
    template <typename T>
    FoobarClass<T> foobar( T value )
    {
        if constexpr( !std::is_pointer_v<T> ) {
          FoobarClass<T> result( std::move( value ) );
          return result;
       }
        else
           static_assert(dependent_false<T>::value, "Geht nicht"); 
    }
    

    tun. hier geklaut

    Oder du musst doch std::enable_if richtig verstehen 😉



  • Ich habe es jetzt übrigens hinbekommen, ohne "Concepts" den Typ T zu einzuschränken:

    // Ganzzahlige Datentypen und Gleitkomma-Datentypen aber keine Zeichen ( 8Bit )
    template <typename T, 
                       typename = std::enable_if_t<(std::is_integral<T>::value || 
                                             std::is_floating_point<T>::value ) &&
                                             !( std::is_same<T, uint8_t>::value || std::is_same<T, int8_t>::value ||
                                             std::is_same<T, unsigned char>::value || std::is_same<T, char>::value ||
                                             std::is_same<T, signed char>::value )>>
     void operator>> ( T & );
    

    Ist natürlich ein ganz schöner Hauklotz, aber typsicher 😃



  • @It0101 sagte in bestimmte Typen von Template ausschließen:

    Ich habe es jetzt übrigens hinbekommen, ohne "Concepts" den Typ T zu einzuschränken:

    // Ganzzahlige Datentypen und Gleitkomma-Datentypen aber keine Zeichen ( 8Bit )
    template <typename T, 
                       typename = std::enable_if_t<(std::is_integral<T>::value || 
                                             std::is_floating_point<T>::value ) &&
                                             !( std::is_same<T, uint8_t>::value || std::is_same<T, int8_t>::value ||
                                             std::is_same<T, unsigned char>::value || std::is_same<T, char>::value ||
                                             std::is_same<T, signed char>::value )>>
     void operator>> ( T & );
    

    Ist natürlich ein ganz schöner Hauklotz, aber typsicher 😃

    Ab C++17 darfst Du auch _v verwenden und das ::value weglassen.

    // Ganzzahlige Datentypen und Gleitkomma-Datentypen aber keine Zeichen ( 8Bit )
    template <typename T, 
       typename = std::enable_if_t<
          (std::is_integral_v<T> || std::is_floating_point_v<T>) &&
          !(
              std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
              std::is_same_v<T, unsigned char> || std::is_same_v<T, char> ||
              std::is_same_v<T, signed char>
           )
       >
    >
    void operator>> (T&);
    

Anmelden zum Antworten