2016-10-18 5 views
4

In meiner API habe ich eine Funktion, die std::istringstream zurückgibt.
Die std::istringstream class ist nicht kopierbar, unterstützt aber das Verschieben auf einen entsprechenden Compiler, es gibt kein Problem, einen lokalen std::istringstream zurückgeben.Problemumgehung für die Rückgabe von nicht kopierbaren Objekt ohne eine Verschiebung ctor

Auf gcc 4.9 gibt es jedoch no support zum Verschieben std::istringstream. Gibt es eine Problemumgehung, die ich std::istringstream verwenden kann, ohne die API aus der Sicht des Benutzers zu ändern?

Die vorgeschlagene Umgehungslösung here, die Verwendung eines unique_ptr<std::istringstream> wird die Semantik der API ändern.

+4

Ich sehe nicht, wie Sie eine Abhilfe haben können, wenn Sie nicht den Rückgabetyp ändern können. – NathanOliver

+0

Nun, sagen wir, ich nehme das Ergebnis mit 'auto' (oder verwende es einfach als temporäres) - damit du etwas anderes zurückgeben kannst. Ich denke jetzt, dass es möglich ist, einen Wrapper mit derselben Schnittstelle zu erstellen und diesen zurückzugeben. –

+0

Der Wrapper würde das gleiche Problem treffen. Sie können den Stream nicht in den Wrapper verschieben und Sie können ihn nicht kopieren. Wie erhalten Sie den Wrapper also aus der Funktion? – NathanOliver

Antwort

0

auf Vollständigkeit und zukünftige Referenz meine eigene Frage zu beantworten.

Das Ziel war es, eine Abhilfe für den gcc zu finden (< 5) Fehler, std::istringstream keinen Zug Ctor bieten, die in den Fällen arbeiten, wo ich den un-kopierbar und (bugly-) unbeweglichen Strom zurückkehren will .
Wie in den Kommentaren erwähnt, kann ich tatsächlich meine Funktionssignatur (mindestens auf gcc < 5) ändern, um ein Proxy-Objekt zurückzugeben, das Kopieren oder Verschieben ermöglicht, ohne die API für Code zu ändern, der auf neueren/anderen Compilern verwendet wird.

Die Idee, vorgeschlagen und von einem Kollegen umgesetzt wird, ist ein Proxy-Objekt um std::istringstream, die eine ähnliche API zu erstellen, sondern bietet auch eine Kopie-Ctor die initialisiert erstellt manuell und eine neues internen std::istringstream aus dem Strom aus kopierten . Dieser Proxy wird nur für die problematischen Compiler verwendet. Der Code in seinem natürlichen Lebensraum ist here.
Hier ist der relevante Teil:

#if !defined(__GNUC__) || (__GNUC__ >= 5) 
    using string_stream = std::istringstream; 
#else 
    // Until GCC 5, istringstream did not have a move constructor. 
    // stringstream_proxy is used instead, as a workaround. 
    class stringstream_proxy 
    { 
    public: 
     stringstream_proxy() = default; 

     // Construct with a value. 
     stringstream_proxy(std::string const& value) : 
     stream_(value) 
     {} 

     // Copy constructor. 
     stringstream_proxy(const stringstream_proxy& other) : 
     stream_(other.stream_.str()) 
     { 
     stream_.setstate(other.stream_.rdstate()); 
     } 

     void setstate(std::ios_base::iostate state) { stream_.setstate(state); } 

     // Stream out the value of the parameter. 
     // If the conversion was not possible, the stream will enter the fail state, 
     // and operator bool will return false. 
     template<typename T> 
     stringstream_proxy& operator >> (T& thing) 
     { 
     stream_ >> thing; 
     return *this; 
     } 


     // Get the string value. 
     std::string str() const { return stream_.str(); } 

     std::stringbuf* rdbuf() const { return stream_.rdbuf(); } 

     // Check the state of the stream. 
     // False when the most recent stream operation failed 
     operator bool() const { return !!stream_; } 

     ~stringstream_proxy() = default; 
    private: 
     std::istringstream stream_; 
    }; 
    using string_stream = stringstream_proxy; 
#endif 
0

Wenn Sie die std::istringstream nicht bewegen können, gibt es keinen großen Weg.

Wenn ein Objekt nicht kopierbar und nicht beweglich ist, können Sie es nicht als Wert zurückgeben. Wenn Sie neue Funktionen unterstützen möchten, erhalten Sie für diese einen neuen Compiler.

In der Zwischenzeit können Sie eine unique_ptr zurückgeben. Wenn Sie wirklich Wert darauf zurückgeben möchten, können Sie einen beweglichen Wrapper mit std::unique_ptr<std::istringstream> zurückgeben und dieselbe Schnittstelle wie einen Istringstream bereitstellen. Dies betrifft jedoch auch den Rückgabetyp.


Es mag verlockend sein, mit der rvalue-Referenz zurückzukehren. Hier ist, was Sie tun können:

struct MyApiClass { 

    std::istringstream&& get_stream() { 
     return std::move(*_stream); 
    } 

private: 
    std::unique_ptr<std::istringstream> _stream; 
}; 

Dann mit Ihrem alten Compiler, können Sie es wie folgt verwenden:

std::istringstream&& stream = myApiClass.get_stream(); 

// use stream as long as myApiClass exists 

Menschen einen neuen Compiler in der Lage sein, es zu benutzen wie folgt aus:

std::istringstream stream = myApiClass.get_stream(); 

// use stream normally 

So ist die API weniger betroffen. Ansonsten kenne ich keine Problemumgehung.

0

Der Weg Klasse zurückzukehren, ohne Bewegung/Copykonstruktor ist die return-Anweisung mit verspannten-init-Liste zu verwenden:

class C { 
    C() = default; 
    C(const C&) = delete; 
    C(C&&) = delete; 
}; 

C make_C() { return {}; } 

int main() { 
    C&& c = make_C(); 
} 

Demo

Leider nur nicht-expliziten Konstruktor für diese in Betracht gezogen werden Initialisierung und std::istringstream haben expliziten Konstruktor.

Eine Abhilfe ist, eine Unterklasse mit nicht explizit Konstruktor zu erstellen:

struct myIStringStream : std::istringstream 
{ 
    myIStringStream() = default; 
}; 

myIStringStream make_istringstream() 
{ 
    return {}; 
} 

int main() 
{ 
    std::istringstream&& iss = make_istringstream(); 
} 

Demo

Verwandte Themen