2016-04-06 16 views
1

Ich schreibe gerade eine Protokollierklasse (nur für die Praxis) und stieß auf ein Problem. Ich habe zwei Klassen: Die Klasse Buffer fungiert als temporärer Puffer und löscht sich selbst in ihrem Destruktor. Und die Klasse Proxy, die eine Buffer-Instanz zurückgibt, so dass ich nicht ständig Buffer() schreiben muss.Verwendung des Kopierkonstruktors erzwingen/Verwendung des Kopierkonstruktors vermeiden

Anyways, hier ist der Code:

#include <iomanip> 
#include <iostream> 
#include <sstream> 
#include <string> 

class Buffer 
{ 
private: 
    std::stringstream buf; 
public: 
    Buffer(){}; 
    template <typename T> 
    Buffer(const T& v) 
    { 
    buf << v; 
    std::cout << "Constructor called\n"; 
    }; 
    ~Buffer() 
    { 
    std::cout << "HEADER: " << buf.str() << "\n"; 
    } 
    Buffer(const Buffer& b) 
    { 
    std::cout << "Copy-constructor called\n"; 
    // How to get rid of this? 
    }; 
    Buffer(Buffer&&) = default; 
    Buffer& operator=(const Buffer&) & = delete; 
    Buffer& operator=(Buffer&&) & = delete; 
    template <typename T> 
    Buffer& operator<<(const T& v) 
    { 
    buf << v; 
    return *this; 
    } 
}; 

class Proxy 
{ 
public: 
    Proxy(){}; 
    ~Proxy(){}; 
    Proxy(const Proxy&) = delete; 
    Proxy(Proxy&&) = delete; 
    Proxy& operator=(const Proxy&) & = delete; 
    Proxy& operator=(Proxy&&) & = delete; 
    template <typename T> 
    Buffer operator<<(const T& v) const 
    { 
    if(v < 0) 
     return Buffer(); 
    else 
     return Buffer(v); 
    } 
}; 

int main() { 
    Buffer(Buffer() << "Test") << "what"; 
    Buffer() << "This " << "works " << "fine"; 
    const Proxy pr; 
    pr << "This " << "doesn't " << "use the copy-constructor"; 
    pr << "This is a " << std::setw(10) << " test"; 
    return 0; 
} 

Hier ist der Ausgang:

Copy-constructor called 
HEADER: what 
HEADER: Test 
HEADER: This works fine 
Constructor called 
HEADER: This doesn't use the copy-constructor 
Constructor called 
HEADER: This is a  test 

Der Code macht genau das, was ich will, aber es hängt von RVO. Ich las mehrere Male, dass Sie nicht auf RVO verlassen sollte, so wollte ich fragen, wie ich kann:

  1. RVO völlig vermeiden, so dass die Copykonstruktor jedes Mal
  2. Vermeiden Sie den Copykonstruktor
genannt wird

Ich habe bereits versucht, den Kopierkonstruktor zu vermeiden, indem ich eine Referenz zurücksende oder die segfaults verschiebe. Ich denke, das ist, weil das temporäre Proxy :: operator < < bei der Rückkehr gelöscht wird.

Ich würde auch an ganz anderen Ansätzen interessiert sein, die ungefähr dasselbe tun.

+2

3. Schreiben Sie Code, der nicht davon abhängt, ob RVO passiert. – juanchopanza

+0

Ich denke, das ist die offensichtliche Wahl. Aber ich wollte einen Kopierkonstruktor vermeiden, der höchstwahrscheinlich nie aufgerufen wird. – Schore

+1

Ihre Klasse sollte sowieso nicht kopierbar oder zuweisbar sein (sie hat ein Datenelement 'stringstream'). Ich weiß wirklich nicht, welches Problem Sie hier lösen wollen. – juanchopanza

Antwort

1

Das scheint wie ein künstliches Problem: Erstens funktioniert der Code, ob RVO aktiviert oder deaktiviert ist (Sie können es mit G ++ mit the no-elide-constructors flag testen). Zweitens kann die Art und Weise, wie Sie die Rückgabe eines Objekts Buffer für die Verwendung mit dem <<-Operator entwerfen, nur durch Kopieren durchgeführt werden: Die Funktion erstellt eine neue Buffer Instanz auf dem Stapel, die dann gelöscht wird, wenn Sie die Funktion verlassen Aufruf (dh zwischen jeder Verkettung in pr << "This " << "doesn't " << "use the copy-constructor";); Aus diesem Grund erhalten Sie einen Segmentierungsfehler, wenn Sie versuchen, dieses Objekt von außerhalb der Funktion zu referenzieren.

Alternativ könnten Sie einen << Operator definieren, um dynamischen Speicher zu verwenden, z. eine Rückkehr unique_ptr<Buffer>:

#include <memory> 

... 

std::unique_ptr<Buffer> operator<<(const T& v) const 
{ 
    if(v < 0) 
     return std::unique_ptr<Buffer>(new Buffer()); 
    else 
     return std::unique_ptr<Buffer>(new Buffer(v)); 
} 

jedoch Ihre ursprüngliche Verkettung Aussagen nicht übersetzbar, dann, weil Proxy::operator<<(const T& v) jetzt ein Objekt vom Typ std::unique_ptr<Buffer> statt Buffer zurückgibt, dass dieses Objekt nicht seine eigene zurück Bedeutung Proxy::operator<<(const T& v) definierte Funktion und so mehrere Verkettungen werden nicht ohne Arbeit explizit de-Referenzierung der zurückgegebene Zeiger:

const Proxy pr; 
std::unique_ptr<Buffer> pb = pr << "This "; 
// pb << "doesn't " << "use the copy-constructor"; // This line doesn't work 
*pb << "doesn't " << "use the copy-constructor"; 

Mit anderen Worten: Ihre Klassen von Natur aus auf das Kopieren verlassen und so, wenn Sie wirklich das Kopieren vermeiden möchten, sollten Sie wirf sie weg und ändere deine Logging-Funktionalitäten komplett neu.


Ich bin sicher, einige schwarz-Magie Voodoo gibt es, die dies möglich aufgerufen werden können, um --- wenn auch die geistige Gesundheit auf Kosten.

+0

Danke! Das fasst es ziemlich gut zusammen. Ich denke, ich bleibe beim Kopieren – Schore