Konstruktor für spezielle Typen "Umleiten"?



  • War leider nichts - er versucht krampfhaft die Iteratorversion aufzurufen bei zwei ints und stolpert über enable_if::type 😞


  • Administrator

    pumuckl schrieb:

    War leider nichts - er versucht krampfhaft die Iteratorversion aufzurufen bei zwei ints und stolpert über enable_if::type 😞

    Kein Wunder, es ist ja auch kein Substitutionsfehler. Die Substitution klappt einwandfrei. Grundsätzlich müsstest du sowas machen:

    template<typename IterT>
    C(IterT, IterT, typename std::iterator_traits<IterT>::iterator_category category = typename std::iterator_traits<IterT>::iterator_category())
    {
    }
    

    Nur dass es problematisch ist, weil es unter MSVC zum Beispiel auch Spezialisierungen für std::iterator_traits für int , double , float , long , short , usw. gibt. Daher funktioniert dies auch nicht ... Es gibt eben keine Möglichkeit einen Iterator zu erkennen.

    Das einzige was du machen könntest, sind gewisse Typen explizit auszuschliessen. Ehm, das müsste dann so gehen:

    struct EmptyType { };
    
    temlate<typename T>
    struct ExcludedTraits
    {
      typename EmptyType Allowed;
    };
    
    template<>
    struct ExcludedTraits<int>
    {
      // int ausschliessen.
    };
    
    class C
    {
    public:
      C(int, long) { std::cout << "funzt" << std::endl; }
    
      template<typename IterT>
      C(IterT, IterT, 
         typename ExcludedTraits<IterT>::Allowed = typename ExcludedTraits<IterT>::Allowed())
      {
        std::cout << "meep" << std::endl;
      }
    };
    
    int main()
    {
      int a = 0;
      int b = 0;
    
      C c(a, b); // sollte gehen ...
    }
    

    Vielleicht kann man es über Typlisten noch ein wenig vereinfachen.
    Aber ob das viel einfacher ist als einfach einen zusätzlichen Konstruktor? 🙂

    Grüssli



  • pumuckl schrieb:

    War leider nichts - er versucht krampfhaft die Iteratorversion aufzurufen bei zwei ints und stolpert über enable_if::type 😞

    Wie wär's mit etwas Code? Ich habe vorhin das hier probiert:

    #include <iostream>
    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits/is_integral.hpp>
    
    void foo(int, long)
    {
    	std::cout << "1\n";
    }
    
    template<typename Iter>
    void foo(Iter beg, Iter end,
    	typename boost::disable_if<boost::is_integral<Iter>,char>::type=0)
    {
    	std::cout << "2\n";
    }
    
    int main()
    {
    	foo(23,42);
    }
    

    und es macht genau das, was es soll ("1" ausgeben).

    Gruß,
    SP



  • Hätte ein normaler User so eine Frage gestellt, wäre schon längst die Frage gekommen: Was willst du eigentlich konkret damit erreichen? Da du aber sicher weiß, was du willst, ist die Farge wohl überflüssig. Mich interessiert aber schon, warum man einen Konstruktor benötigt, der ein int und ein long als Parameter hat. Schließlich gilt immer sizeof(int) <= sizeof(long) , also könntest du auch zwei long als Parameter nehmen.

    Gruß
    Don06


  • Administrator

    Oh Gott, ich trottel ... klar funktioniert dies! Ich habe boost::enable_if<cond, char> mit Select<cond, char, void> verwechselt.

    Ich ziehe mein vorheriges Posting zurück ... peinlich, peinlich ...

    Grüssli



  • Ich bin dabei ein Containertemplate zu schreiben. Wie bei std::vector solls einen Ctor für ein iterator-Range geben, sowie einen für size_type und T. letzterem kann man bei std::vector für T=int oder T=long usw. zwei int übergeben, bzw. sollte man. In der Standardbibliothek die ich hier habe ist es so gelöst, dass der Range-Ctor bei Iter=int sich so verhält wie der NxT-Ctor. Ich wollte nach eier Lösung suchen wo gleich der richtige Ctor gewählt wird.

    Mein Ansatz bisher:

    template <class T>
    class MyContainer /*: boost::equality_comparable<MyContainer<T> >*/
    {
      template <class Iter>
      struct DisableInt
      {
        typedef typename boost::disable_if<
          boost::is_integral<Iter>, char
        >::type type;
      };
    
    public:
    
      MyContainer();
      explicit MyContainer( size_type n, const_value_type& value= T());
      template <class InputIterator >
      MyContainer(InputIterator first, InputIterator last, 
             typename DisableInt<InputIterator>::type = 0);
    };
    

    Beim Compilieren gibts dann entsprechend eine Fehlermeldung:

    c:\development\projects\lib_pumu\src\pumu\container\mycontainer.hpp(26) : error C2039: 'type' : is not a member of 'boost::disable_if<Cond,T>'
    1>        with
    1>        [
    1>            Cond=boost::is_integral<int>,
    1>            T=char
    1>        ]
    1>        c:\development\projects\lib_pumu\test\pumu\container\testmycontainer.cpp(81) : see reference to class template instantiation 'pumu::container::MyContainer<T>::DisableInt<Iter>' being compiled
    1>        with
    1>        [
    1>            T=int,
    1>            Iter=int
    1>        ]
    

  • Administrator

    Wirf das struct DisableInt weg. So wird SFINAE nicht funktionieren. Der Substitutionsfehler passiert nicht im Funktionskopf, sondern in der Struktur DisableInt . Der Fehler muss aber im Funktionskopf passieren, damit es funktioniert.

    Grüssli



  • Ja hatte da n Denkfehler. Hiermit gehts:

    template <class Iter>
    struct DisableInt : public boost::disable_if<boost::is_integral<Iter>, char>
    {};
    


  • Dravere schrieb:

    Nur dass es problematisch ist, weil es unter MSVC zum Beispiel auch Spezialisierungen für std::iterator_traits für int , double , float , long , short , usw. gibt. Daher funktioniert dies auch nicht ... Es gibt eben keine Möglichkeit einen Iterator zu erkennen.

    Das ist komisch. Weisst du wozu es diese Spezialisierungen gibt?

    Aber mal davon abgesehen... könnte man sich nicht zu nutze machen dass in dem Fall Iter und std::iterator_traits<Iter>::value_type der selbe Typ sind?

    Ich muss sagen, als ich enable_if vorgeschlagen habe, bin ich einfach davon ausgegangen, dass es recht einfach möglich sein muss is_iterator zu implementieren, da es ja std::iterator_traits gibt.

    Irgendwie ziemlich doof dass es doch nicht so einfach ist 😞



  • hustbaer schrieb:

    Irgendwie ziemlich doof dass es doch nicht so einfach ist 😞

    Ich schätze mal wenns so einfach wäre gäbs bei boost auch ein is_iterator oder ähnliches. Es gibt in der Mailing-List schon eine längere Diskussion zu dem Thema:

    http://lists.boost.org/Archives/boost/2004/08/70530.php


  • Administrator

    hustbaer schrieb:

    Das ist komisch. Weisst du wozu es diese Spezialisierungen gibt?

    Nicht die Bohne einer Ahnung! Es steht nicht mal hier etwas:
    http://msdn.microsoft.com/en-us/library/zdxb97eh.aspx

    Ich weiss es selber nur, weil ich mal etwas über std::iterator_traits lösen wollte und dabei voll auf die Schnauze gefallen bin. Vor allem habe ich zuerst ziemlich lange gesucht, bis ich gemerkt habe, dass da zusätzliche Spezialisierungen vorhanden sind. Ich wäre niemals auf diese Idee gekommen 😉

    Hey ... moment mal ... würde sowas nicht gehen?

    template<typename CategoryT>
    struct isValidIteratorCategory
    {
      enum ( value = 0 };
    };
    
    template<>
    struct isValidIteratorCategory<std::input_iterator_tag>
    {
      enum { value = 1; };
    };
    
    template<>
    struct isValidIteratorCategory<std::output_iterator_tag>
    {
      enum { value = 1; };
    };
    
    template<>
    struct isValidIteratorCategory<std::forward_iterator_tag>
    {
      enum { value = 1; };
    };
    
    template<>
    struct isValidIteratorCategory<std::bidirectional_iterator_tag>
    {
      enum { value = 1; };
    };
    
    template<>
    struct isValidIteratorCategory<std::random_access_iterator_tag>
    {
      enum { value = 1; };
    };
    
    template<typename IterT>
    struct IsIterator
    {
      enum { value = isValidIteratorCategory<typename std::iterator_traits<IterT>::iterator_category>::value };
    };
    
    template<typename T>
    struct IsIterator<T*>
    {
      enum { value = 1; };
    };
    
    template<typename T>
    struct IsIterator<T const*>
    {
      enum { value = 1; }
    };
    

    Oder überseh ich etwas?

    Grüssli


  • Mod

    Dravere schrieb:

    Oder überseh ich etwas?

    class Alien {};
    int main()
    {
        cout << IsIterator<Alien>::value;
    }
    

    Das ist dann möglicherweise der Grund, weshalb Spezialisierungen für int&co. eingeführt werden, um nicht schon iterator_traits<int>::iterator_category auszusteigen, sondern ggf. das Ergebnis per sfinae zu verwerten.

    vielleicht so

    typedef char no;
    typedef no yes[2];
    no& tag_test(...);
    template <typename T> yes& tag_test(const T&, iterator_traits<T>::iterator_category*=0);
    template <typename T>
    struct has_tag : mpl::bool_<sizeof(tag_test(*(T*)0))==sizeof(yes)> {};
    
    typedef mpl::vector<input_iterator_tag,output_iterator_tag,forward_iterator_tag,random_access_iterator_tag> valid_tags;
    template <typename T, bool = has_tag<T>::value>
    struct is_iterator : mpl::contains<valid_tags,T> {};
    template <typename T>
    struct is_iterator< T, false> : mpl::bool_<false> {};
    


  • Wenn ich das richtig sehe brauch ich noch nichtmal den Dummy-Parameter - folgendes compiliert bei mir:

    template <class T>
    struct DisableInt : public boost::disable_if<boost::is_integral<T>, T>{};
    
    void foo(int i, short l)
    {
       cout << "integral" << endl;
    }
    
    template<class I>
    void foo(I first, typename DisableInt<I>::type last)
    {
      cout << "iterator" << endl;
    }
    
    int main()
    {
       int a[] = {1, 2, 3, 4, 5};
       std::vector<int> vec(a, a+5);
    
       foo(3, 6); //integral
       foo(a, a+5); //iterator
       foo(vec.begin(), vec.end()); //iterator
    }
    

    Gibts irgendwelche Fallen in die ich da tappen könnte oder ist das ok?


  • Mod

    foo(0.0,0.0);
    

    (is_arithmetic ist etwas besser, aber auch hier finden sich Ausnahmen)
    Compilierbarkeit als solche ist generell nicht das Problem, da es bei nicht-Template- vs. Templatefunktion nicht zu Mehrdeutigkeiten kommen kann.

    Den 2. Typen nicht zu deduzieren, erlaubt Konvertierungen für diesen. Zum Beispiel könnte man einen const_iterator mit einem nicht-const-Iterator kombineren. Das ist zumindest unerwartet und könnte auf einen Fehler beim Aufruf hindeuten. Zudem gibt einige defekte Implementationen, die die Initialiserung von Iteratoren mit Zeigern zulassen, so dass dann so etwas wie

    foo(it,0);
    

    zulässig wäre. An der Stelle halte ich den Verzicht auf den Dummy-Parmeter für ungünstig, man verzichtet auf eine rigidere Prüfung und das Interface wird selbst unübersichtlich und schwer verständlich. Im Falle eines Dummy-Parameters ist dagegen klar, was echte Parameter sind, und welcher Teil nur zur Überprüfung/Overloadauflösung dient.



  • camper schrieb:

    foo(0.0,0.0);
    

    (is_arithmetic ist etwas besser, aber auch hier finden sich Ausnahmen)

    Da es bei der nicht-Templatefunktion um einen myContainer::size_type geht, der immer integral ist, darf 0.0 gerne auf Fehler laufen, weis kein Iterator ist.

    camper schrieb:

    Den 2. Typen nicht zu deduzieren, erlaubt Konvertierungen für diesen.

    Guter Einwand, also doch den dummy. Danke 🙂


Anmelden zum Antworten