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; }
-
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?
-
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 << iveceine 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++