Vermeidung mehrdeutiger Templates



  • Ich wollte eine Templatefunktion schreiben, die mit boost::range beliebige Container ausgeben kann (sind meine ersten Spielereinen mit boost::range). Meine erste naive Idee sah so aus:

    template<class Container>
    ostream& operator << (ostream& o, const Container& c)
    {
    	typedef typename boost::range_const_iterator<Container>::type iterator;
    
    	for(iterator i = boost::begin(c), end = boost::end(c); i != end; ++i)
    		o << *i << ", ";
    }
    

    Allerdings liefert das Probleme, wenn schon ein überladener op<< existiert, z.B. bei std::string (mehrdeutiger Aufruf). Wenn ich mit boost::enable_if und is_same gespielt und string ausgeschlossen habe, war das nächste Problem bei std::char_traits. Außerdem denk ich, ist es der falsche Weg, explizit alle problematischen Typen auszuschließen.
    Ich suche also eine möglichst einfache Möglichkeit, per enable_if oder wie auch immer die Typen auszuschließen, bei denen schon ein op<< existiert.
    Kennt jemand eine gute Lösung für mein Problem?



  • In deinem Fall würde ich mir einfach zunutze machen, dass boost::iterator_range bereits eine Konvertierung von Containern erlaubt und statt eines Containers dem op<< einfach eine Range als zweiten Parameter geben.
    Bei std::string gibt es dann schon eine direkte Überladung von op<<, und die implizite Konvertierung in eine iterator_range wird nicht in Betracht gezogen.

    #include <boost/range/iterator_range.hpp>
    #include <vector>
    #include <iostream>
    
    template <class ForwardTraversalIterator>
    std::ostream& operator<< (std::ostream& os, boost::iterator_range<ForwardTraversalIterator> const& range)
    {
      typedef typename boost::iterator_range<ForwardTraversalIterator>::iterator iter_t;
      iter_t it = range.begin();
      os << '[' << *(it++);
      for (iter_t end = range.end(); it != end; ++it)
      {
        os << ',' << *it;
      }
      os << ']';
      return os;
    }
    
    int main()
    {
      std::vector<int> ivec;
      for(int i = 1; i < 200; i *= 2)
      {
        ivec.push_back(i);
      }
    
      std::cout << ivec << std::endl;
    }
    

    Getestet auf codepad.org


  • Mod

    pumuckl schrieb:

    Bei std::string gibt es dann schon eine direkte Überladung von op<<, und die implizite Konvertierung in eine iterator_range wird nicht in Betracht gezogen.

    Die wird in keinem Fall in Betracht gezogen, wenn das Argument für die Deduktion des Templatearguments herangezogen wird.

    Getestet auf codepad.orgTrotzdem kein Standard C++.

    Eine simple Lösung wäre das Funktionstemplate allgemeiner zu gestalten als die speziellen Überladungen z.B. für std::string.

    template<class charT, class traits, class Allocator>
    basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT,traits,Allocator>& str);
    

    ist die Überladung für std::string.

    template<class Container>
    ostream& operator << (ostream& o, const Container& c)
    

    ist diesbezüglich ungeordnet.

    Mit

    template<class charT, class traits, class Container>
    basic_ostream<charT,traits>& operator << (basic_ostream<charT,traits>& o, const Container& c)
    

    besteht hingegen kein Problem, da die Überladung für std::string spezieller ist.



  • camper schrieb:

    Getestet auf codepad.orgTrotzdem kein Standard C++.

    Weils boost benutzt oder noch aus einem anderen Grund?


  • Mod

    pumuckl schrieb:

    camper schrieb:

    Getestet auf codepad.orgTrotzdem kein Standard C++.

    Weils boost benutzt oder noch aus einem anderen Grund?

    Weil, wie ich oben schon sagte, in

    std::cout << ivec
    

    eine Konvertierung in

    boost::iterator_range<ForwardTraversalIterator>
    

    in ausgeschlossen ist, weil ivec selbst zur Deduktion des Templateparameters benutzt wird (und diese Deduktion ebenso fehlschlagen muss).

    gcc 4.4.3 schrieb:

    test.cpp: In function ‘int main()’:
    test.cpp:27: error: no match for ‘operator<<’ in ‘std::cout << ivec’
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:108: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& ()(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:117: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (
    )(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:127: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& ()(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:165: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:169: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:173: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/bits/ostream.tcc:91: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:180: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/bits/ostream.tcc:105: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:191: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:200: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:204: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:209: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:213: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:221: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/ostream:225: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(const void
    ) [with _CharT = char, _Traits = std::char_traits<char>]
    /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4/bits/ostream.tcc:119: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]

    es wird also kein passender Kandidat gefunden, wie zu erwarten ist.



  • Ok vielen Dank für die Antworten.
    pumuckls Lösung funktioniert wie angedeutet leider nicht, compers Variante mit der allgemeineren Signatur schon.
    Die Funktion sieht jetzt so aus:

    template<class charT, class traits, class  Container>
    basic_ostream<charT,traits>& operator << (basic_ostream<charT,traits>& o, const Container& c)
    {
    	typedef typename boost::range_const_iterator<Container>::type iterator;
    
    	iterator i = boost::begin(c), end = boost::end(c);
    	o << '[' << *(i++);
    	for(; i != end; ++i)
    		o << ", " << *i;
    	o << ']';
    
    	return o;
    }
    

    Und funktioniert wie gewünscht (ich meine sogar, verstanden zu haben, wieso).
    Man lernt eben echt nie aus in C++ 😉


Anmelden zum Antworten