2010-07-12 6 views
5

Das folgende Segment zeigt mein Problem: (Kompilierungsfehler auf GCC)Frage zum String mit :: swap() mit Provisorien

stringstream ss; 
string s; 
ss << "Hello"; 

// This fails: 
// s.swap(ss.str()); 

// This works: 
ss.str().swap(s); 

Mein Fehler:

constSwap.cc:14: error: no matching function for call to 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >::swap(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)' 
basic_string.tcc:496: note: candidates are: void std::basic_string<_CharT, _Traits, _Alloc>::swap(std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 

Während ich, dass str verstehen () in stringstream gibt ein temporäres zurück, es ergibt keinen Sinn und es war nicht sofort ersichtlich, dass ich den Swap auf dem temporären mit der lokalen Variable anstelle meines ersten Instinktes als Parameter aufgerufen hätte.

Offensichtlich funktioniert die direkte Zuweisung besser, und neuere C++ - Standards haben eine Bewegungssemantik, die perfekt ist, aber diese sind für meine Implementierung nicht verfügbar.

Visual Studio gibt dieses Problem nicht, weil es über den C++ - Standard entspannt ist. Ich verstehe schon den ganzen const Verweis auf eine temporäre Sache (die ich vermute, ist der Grund für meine Kompilierungsfehler).

Meine Frage: Kann mir jemand erklären, wenn dies die einzige Lösung ist, und mir vielleicht erklären, wie ich in Zukunft darüber nachdenken soll, damit ich ähnliche Probleme erkennen und bearbeiten kann?

(Wenn niemand irgendwelche großen Einsichten hat ich zumindest dieses Posting hier für Menschen mit ähnlichen Problemen)

+0

Eine andere Anmerkung, für einen Swap zu arbeiten muss man in einer nicht-const Referenz gehen. Und alles, was ich habe, ist (da es eine vorübergehende Rückkehr ist) ein konstanter Wert. Also verstehe ich ... es hat einfach keinen logischen Sinn ergeben, dass umgekehrt legal ist (obwohl ich verstehe, warum das auch so ist). – Marius

+1

Kompilieren Sie den Code in VS2010, es hat 2 Varianten von Swap. Man nimmt Bezug und den anderen rvalue Verweis. Das könnte der Grund sein, warum es kompiliert wird. – Jagannath

+0

@Jagannath: Ich benutze VS2005 im Moment, leider steht mir keine dieser neueren Techniken zur Verfügung. – Marius

Antwort

2

Nach der Swap-with-temporärer genutzt habe Idiom genug Zeit, mit Zeilen wie

std::vector<int>().swap(v); // clear and minimize capacity 

oder

std::vector<int>(v).swap(v); // shrink to fit 

dies scheint nicht so fehl am Platz. Es ist normal, Swap als eine Member-Funktion eines temporären Objekts aufzurufen. Natürlich ist es nicht so idiomatisch, Swap zu verwenden, um eine standardkonstruierte Zeichenkette zu füllen, anstatt einen Kopierkonstruktor zu verwenden, wie bereits erwähnt.

+0

Dies ist am besten meine Frage adressiert. Ich denke, ich sollte mich erst an das Idiom gewöhnen. – Marius

7

Sie können keine temporäre auf eine nicht konstante Referenz binden. Aus diesem Grund kann das von ss.str() zurückgegebene temporäre nicht an std::string::swap übergeben werden, das erwartet, seinen Parameter zu ändern (dafür nimmt es sein Argument mit non-const&).

Die zweite Version funktioniert, da Sie eine Memberfunktion für das temporäre Objekt aufrufen, das zulässig ist.

Aber warum möchten Sie in erster Linie tauschen? Normalerweise ist ein einfaches:

sollte gut genug sein. Dies ist nicht weniger effizient als der Austausch (zumindest in C++ 0x mit Bewegungssemantik), aber gleichzeitig viel besser lesbar.

+0

+1 für das Tippen schneller als ich. – Cogwheel

+0

'std :: string s (s.str())' ist langsamer als der Swap, weil es den String kopieren muss (außer in C++ 0x, das eine Bewegungssemantik mit '&&' references hat). –

+0

Sie haben sicherlich Recht, ich habe meine Antwort entsprechend bearbeitet. – hkaiser

1

Der Grund dafür, dass Sie das temporary as Argument nicht an swap übergeben können, ist, dass das Argument von einer nichtkonstanten Referenz übergeben wird. Und Provisorien können nur durch Const-Referenzen gebunden werden. Dieser erstreckt sich über §8.5.3, mit dem entsprechenden Wesen in Absatz 5, zweite Kugel:

§8.5.3 A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • [bullet one, does not apply here: binding to non-const ref ]

  • Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).

Der Grund, warum in die entgegengesetzte Richtung, um den Anruf zu schreiben funktioniert, ist, dass der Standard aufrufen können auf temporäre Mitgliederfunktionen mutiert Objekte.

§3.10/10 An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]

Die Argumentationslinie, die Sie für die Zukunft verlangen, ist, dass, während Sie eine temporäre durch seine eigene Funktionen ändern können, können Sie es nicht auf eine Funktion oder Methode übergeben kann, die es ändern könnte (mit der durch nicht-const Referenz)