2016-03-27 20 views
-3

Lassen Sie uns sagen, dass ich eine Klasse:Nicht Freund einzeiligen operator + Überlastung basierend auf Operator + =

class A 
{ 
    //... 
}; 

Mit gut operator += definiert:

A& operator +=(const A&) {return *this;} //class without members 

Lassen Sie uns also auch versuchen, zu überlasten operator+ (als Nicht-Freund). Ich will nicht, Klassennamen verwenden Konstruktor von temporären Objekt aufzurufen (ein bisschen wollen, dass diese generischen Code machen):

A operator +(const A& other) const 
{ 
    return auto(*this)(*this) += other; //error: invalid use of auto 
//   /\/\/\/\/\ /\  
//  type of *this || 
//     constructor call 
} 

auto hier nicht gut. Lass es uns versuchen decltype.

A operator +(const A& other) const 
{ 
    return decltype(*this)(*this) += other; //error: 'A& A::operator+=(const A&)' discards 
//   /\/\/\/\/\/\/\ /\      qualifiers [-fpermissive] return 
//   type of *this ||      decltype(*this)(*this) += other; 
//     constructor call         ^
} 

Diese verwalten die Art von *this zu bekommen, aber operator+ ist const deklariert, so bekamen wir const A abgeleitet (das ist, was ich dachte). Also lass uns weitergehen:

A operator +(const A& other) const 
{ 
    return typename std::remove_const<decltype(*this)>::type(*this) += amount; 
    //same error as previous 
} 

Jetzt wurde ich verrückt. Selbst wenn ich die Konstanz entfernt habe, verwirft es immer noch Qualifier. Nun, vielleicht ist das nur, weil ich nur CASTING gemacht habe. So dumm. Um einen Konstruktor aufzurufen, müsste ich Code erzeugen, der (neben dem Typ) einen :: Constructor hat (also habe ich sogar versucht, einen Alias ​​für Konstruktor zu erstellen, aber an dieser Stelle war ich so hart ausgefallen). Mein gebrochenes Gehirn gab, doch Rest meines Bewußtseins gab mir eine Lösung (die schriftlich generisch ist, so das ist gut):

// outside of class 
template<class A> 
inline A&& make_rvalue(A copy) 
{ 
    return std::move(copy); 
} 

// inside of class 
A operator +(const A& other) const 
{ 
    return make_rvalue(*this) += other; // such generic code 
} 

Das ist, was ich mit beendet. Aber gibt es einen Trick, der keinen anderen Funktionsanruf beinhaltet?

EDIT: Ich weiß, klassische Methoden, dies zu tun, aber was ich suche ist unten beschrieben:

  • Betreiber reduziert wird, um {return /*...*/;}
  • keine Namen von Klassenmethoden oder globale Funktionen beinhalten
  • berücksichtigt Überladungen mit anderen Typen - es kann keine Argument (en) nach Wert nehmen, da class A != class B, Argument int über const int& hilft nicht viel mit Matrix Klasse (aber Proxy-Operator, der Zieloperator mit ausgetauscht ar aufgerufen gumente ist OK)
  • nimmt (möglich) Reihenfolge der Operation zu berücksichtigen ([email protected] != [email protected]), wobei beide sollten gleiche return-Anweisung
  • return-Anweisung exacly für gegebene [email protected] gleich sein sollte, ist operator += jede Klasse, die überlastet haben sollte
+1

Warum nicht einfach das Argument nach Wert anstelle von const ref nehmen? – Useless

+2

Keine Ahnung, wovon du sprichst. –

+2

Dieser 'Operator +' ist ** nicht ** generisch; Es wird in Objekten vom Typ "A" gerendert. Es gibt keinen Grund, den Typ A zu meiden, da Sie genau wissen, was es ist. Und selbst im generischen Code kennen Sie den Typ, mit dem Sie es zu tun haben. All diese Umschreibung ist sinnlos. –

Antwort

1

Wenn Sie eine Funktion wie folgt zu erstellen:

template <typename T> 
T add(T temp,const T &b) 
{ 
    temp += b; 
    return temp; 
} 

Sie können es wie folgt verwenden:

A operator+(const A& other) const { return add(*this,other); } 
+0

Ich kenne diesen sehr gut, benutze diese Version seit C++ 11 herauskam. Was ich suche, ist die Version von 'return /*...*/;' – xinaiz

+0

@BlackMoses: Ok, ich habe meine Antwort so geändert, dass sie übereinstimmt. –

1

Ich werde aufschreiben, dass diese ganze Linie des Denkens/Kodierens in die falsche Richtung geht. Sie wissen bereits, dass (in diesem Fall) der Rückgabetyp A ist. Ihr Code scheint eine Menge Arbeit darin zu stecken, A auf die komplexeste, indirekte Weise zu sagen.

A operator +(const A& other) const { 
    A ret {*this}; 
    ret += other; 
    return ret; 
} 

Da Sie scheinen wirklich wollen C++ 11-Funktionen verwenden (auch in diesem Fall allerdings fast keiner von ihnen eine wirkliche Hilfe zur Verfügung stellt) ich einen verspannten initializer verwendet, so ist es etwas nutzt aus C++ 11. Und nein, das ist keine Zeile, aber es ist lesbar.

Beachten Sie, dass derselbe Stil auch im generischen Code in Ordnung ist. Zum Beispiel in einer Nicht-Mitglied Überlastung von operator+, könnten wir so etwas wie diese:

template <class T> 
T operator+(T const &a, T const &b) { 
    T ret {a}; 
    ret += b; 
    return ret; 
} 

Dies kann ein bisschen auch wenn vereinfacht werden:

template <class T> 
T operator+(T ret, t const &b) { 
    ret += b; 
    return ret; 
} 

Wieder verlieren wir genau nichts aus der Tatsache, dass ältere Compiler den Code problemlos akzeptieren können und werden.

+0

Ok, aber wenn Operator + beinhaltet zwei Arten, und die Reihenfolge der Operation macht großen Unterschied, denke über Anruf von möglichen Proxy-Operator, die tatsächlichen Operator mit swaped Argumente aufruft - einfache Vorlagen nicht wissen, was eine gute Bestellung ist, und solche Proxy-Vorlage würde Genau wie Originalvorlage, aber ohne zu wissen, ob es sich tatsächlich um einen Proxy handelt oder nicht. Zu vieldeutig, um damit zu arbeiten. – xinaiz

+0

@BlackMoses - ja, wenn die Frage anders war, könnte die Antwort anders sein. Aber es ist nicht. –

Verwandte Themen