Ich sehe hin und wieder Fragen zu SO über das Überladen des Kommaoperators in C ++ (hauptsächlich unabhängig von der Überladung selbst, aber Dinge wie der Vorstellung von Sequenzpunkten), und ich frage mich:
Wann sollten Sie das Komma überladen? Was sind einige Beispiele für seine praktische Verwendung?
Ich kann mir einfach keine Beispiele vorstellen, bei denen ich so etwas gesehen habe oder brauchte
foo, bar;
Ich bin gespannt, wann (wenn überhaupt) dies tatsächlich verwendet wird.
c++
function
operator-overloading
user541686
quelle
quelle
Antworten:
Lassen Sie uns die Betonung ein wenig ändern auf:
Die Antwort: Niemals.
Die Ausnahme: Wenn Sie eine Vorlagen-Metaprogrammierung durchführen,
operator,
befindet sich ganz unten in der Rangliste der Operatoren eine spezielle Stelle, die sich zum Erstellen von SFINAE-Guards usw. als nützlich erweisen kann.Die einzigen zwei praktischen Anwendungen, die ich beim Überladen gesehen habe,
operator,
sind beide in Boost :quelle
operator,
? Das klingt wirklich interessant.Ich habe den Komma-Operator verwendet, um Karten mit mehreren Indizes zu indizieren.
enum Place {new_york, washington, ...}; pair<Place, Place> operator , (Place p1, Place p2) { return make_pair(p1, p2); } map< pair<Place, Place>, double> distance; distance[new_york, washington] = 100;
quelle
operator[]
. Einige haben vorgeschlagen, dass mehrere Parameter verwendet werden können: siehe Evolution Defect Report 88 .distance[{new_york, washington}]
funktioniert ohne etwas zu überladen. Ein zusätzlicher Satz Klammern ist ein kleiner Preis, um etwas so Böses zu vermeiden!foo(new_york, washington)
, die zwei separate Stellen als Argumente verwenden sollte?Boost.Assign verwendet es, damit Sie Dinge tun können wie:
vector<int> v; v += 1,2,3,4,5,6,7,8,9;
Und ich habe gesehen, dass es für skurrile Sprach-Hacks verwendet wird. Ich werde sehen, ob ich welche finden kann.
Aha, ich erinnere mich an eine dieser skurrilen Anwendungen: das Sammeln mehrerer Ausdrücke . (Warnung, dunkle Magie.)
quelle
push_back
für diese 8 Werte ist, aber es sieht so aus, als würde 9 zu a hinzugefügtvector<int>
, was keinen Sinn ergibt. Ehrlich gesagt ist dies ein starkes Gegenargument dafür, dass Boost eine "qualitativ hochwertige Bibliothek" ist. Der Code sollte klar und offensichtlich sein. Andernfalls könnte man auch so etwas implementierenT& operator--(int){ delete this; return *this; }
, was wahrscheinlich auch gut funktionieren würde. Es ist für andere einfach nicht offensichtlich, was passiert.operator<=
. So konnten Sie coolen Code wie schreibenstr <= "foo";
. Außer es ist überhaupt nicht cool, wenn die nächste Person, die Ihren Code liest, sagt "Was zur Hölle?" und es wird völlig uncool, wenn Sie zum ersten Mal eine Woche lang umsonst debuggen, weil jemand so etwas nicht wusste und schriebif(str <= "bar")
.Das Komma hat eine interessante Eigenschaft, da es einen Parameter vom Typ annehmen kann
void
. Wenn dies der Fall ist, wird der integrierte Kommaoperator verwendet.Dies ist praktisch, wenn Sie feststellen möchten, ob ein Ausdruck den Typ void hat:
namespace detail_ { template <typename T> struct tag { static T get(); }; template <typename T, typename U> tag<char(&)[2]> operator,(T, tag<U>); template <typename T, typename U> tag<U> operator,(tag<T>, tag<U>); } #define HAS_VOID_TYPE(expr) \ (sizeof((::detail_::tag<int>(), \ (expr), \ ::detail_::tag<char>).get()) == 1)
Ich lasse den Leser als Übung herausfinden, was los ist. Denken Sie daran, dass
operator,
Mitarbeiter auf der rechten Seite.quelle
Ähnlich wie im Boost.Assign- Beispiel von @ GMan überlädt Blitz ++ den Komma-Operator, um eine bequeme Syntax für die Arbeit mit mehrdimensionalen Arrays bereitzustellen . Zum Beispiel:
Array<double,2> y(4,4); // A 4x4 array of double y = 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1;
quelle
In SOCI - The C ++ Database Access Library wird es für die Implementierung des eingehenden Teils der Schnittstelle verwendet:
sql << "select name, salary from persons where id = " << id, into(name), into(salary);
Aus den Begründungs-FAQ :
quelle
Ich verwende den Komma-Operator zum Drucken der Protokollausgabe. Es ist eigentlich sehr ähnlich,
ostream::operator<<
aber ich finde den Komma-Operator tatsächlich besser für die Aufgabe.Also habe ich:
template <typename T> MyLogType::operator,(const T& data) { /* do the same thing as with ostream::operator<<*/ }
Es hat diese schönen Eigenschaften
Der Kommaoperator hat die niedrigste Priorität. Wenn Sie also einen Ausdruck streamen möchten, werden die Dinge nicht durcheinander gebracht, wenn Sie die Klammer vergessen. Vergleichen Sie:
myLog << "The mask result is: " << x&y; //operator precedence would mess this one up myLog, "The result is: ", x&y;
Sie können sogar problemlos Vergleichsoperatoren im Inneren mischen, z
myLog, "a==b: ", a==b;
Der Kommaoperator ist optisch klein. Es bringt das Lesen nicht durcheinander, wenn viele Dinge zusammengeklebt werden
myLog, "Coords=", g, ':', s, ':', p;
Es stimmt mit der Bedeutung des Kommaoperators überein, dh "dies drucken" und dann "das drucken".
quelle
Eine Möglichkeit ist die Boost Assign- Bibliothek (obwohl ich mir ziemlich sicher bin, dass einige Leute diesen Missbrauch eher als eine gute Verwendung betrachten würden).
Boost Spirit überlastet wahrscheinlich auch den Komma-Operator (es überlastet fast alles andere ...)
quelle
In diesem Sinne wurde mir eine Github-Pull-Anfrage mit Überladung durch Komma-Operatoren gesendet. Es sah ungefähr so aus, als würde man folgen
class Mylogger { public: template <typename T> Mylogger & operator,(const T & val) { std::cout << val; return * this; } }; #define Log(level,args...) \ do { Mylogger logv; logv,level, ":", ##args; } while (0)
dann kann ich in meinem Code Folgendes tun:
Log(2, "INFO: setting variable \", 1, "\"\n");
Kann jemand erklären, warum dies ein guter oder schlechter Anwendungsfall ist?
quelle
... << "This is a message on line " << std::to_string(__LINE__) << " because variable a = " << std::to_string(a) << " which is larger than " << std::to_string(limit) << "\n"
. Dies ist sehr häufig bei der Meldung von Fehlern oder beim Erstellen von Nachrichten für Ausnahmen. Ich bin mir nicht sicher, ob Komma die einzige Wahl war: Jeder andere Operator hätte dies zum Beispieloperator+
oderoperator|
oderoperator&&
oder sogaroperator<<
selbst erreichen können. Aber es ist ein interessanter Fall.Eine der praktischen Anwendungen besteht darin, sie effektiv mit variablen Argumenten im Makro zu verwenden. Variable Argumente waren übrigens früher eine Erweiterung in GCC und jetzt Teil des C ++ 11-Standards.
Angenommen, wir haben ein
class X
, das ein Objekt vom Typ hinzufügtA
. dhclass X { public: X& operator+= (const A&); };
Was passiert , wenn wir ein oder mehr Objekte hinzufügen mögen die
A
inX buffer;
?Zum Beispiel,
#define ADD(buffer, ...) buffer += __VA_ARGS__
Über dem Makro, wenn verwendet als:
dann wird es erweitert zu:
Daher ist dies ein perfektes Beispiel für die Verwendung eines Kommaoperators, da die variablen Argumente mit demselben erweitert werden.
comma
Um dies zu beheben, überladen wir den Operator und wickeln ihn+=
wie folgt umX& X::operator, (const A& a) { // declared inside `class X` *this += a; // calls `operator+=` }
quelle
template<typename ... A> X& ADD(X& buff, A ... args) { int sink[]={ 0,(void(buff+=args),0)... }; return buff;}
. Hinweis: Sie müssen wahrscheinlich die Optimierung der Senke mit einer(void) sink;
Anweisung verhindern. Dies weicht dem Makro aus, das imo sogar noch besser istHier ist ein Beispiel aus der OpenCV-Dokumentation ( http://docs.opencv.org/modules/core/doc/basic_structures.html#mat ). Der Komma-Operator wird für die cv :: Mat-Initialisierung verwendet:
// create a 3x3 double-precision identity matrix Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);
quelle