Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are move semantics for a class containing a std::stringstream causing compiler errors?

Tags:

How can I make this simple class movable? What I thought was correct just produces a wall of errors...

#include <iostream> #include <sstream> #include <utility>  class message {     public:         message() = default;          // Move constructor         message( message &&other ) :             stream_( std::move( other.stream_ ) ) // Nope         {}          // Move assignment         message &operator=( message &&other )         {             if ( this != &other )             {                 stream_ = std::move( other.stream_ ); // Nope #2             }             return *this;         }      private:         message( const message & ) = delete;         message &operator=( const message & ) = delete;          std::stringstream stream_;         // Other member variables omitted };  int main() {     message m;      return 0; } 

Compile:

$ g++ --version g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 $ g++ -Wall -Wextra -std=c++0x move.cpp -o move 

... and get a wall of errors about the copy assignments being called for various base classes of the stringstream.

move.cpp: In constructor ‘message::message(message&&)’: move.cpp:12:40: error: use of deleted function ‘std::basic_stringstream<char>::basic_stringstream(const std::basic_stringstream<char>&)’ In file included from move.cpp:2:0: /usr/include/c++/4.6/sstream:483:11: error: ‘std::basic_stringstream<char>::basic_stringstream(const std::basic_stringstream<char>&)’ is implicitly deleted because the default definition would be ill-formed: /usr/include/c++/4.6/sstream:483:11: error: use of deleted function ‘std::basic_iostream<char>::basic_iostream(const std::basic_iostream<char>&)’ In file included from /usr/include/c++/4.6/iostream:41:0,                  from move.cpp:1: /usr/include/c++/4.6/istream:774:11: error: ‘std::basic_iostream<char>::basic_iostream(const std::basic_iostream<char>&)’ is implicitly deleted because the default definition would be ill-formed: /usr/include/c++/4.6/istream:774:11: error: use of deleted function ‘std::basic_istream<char>::basic_istream(const std::basic_istream<char>&)’ /usr/include/c++/4.6/istream:57:11: error: ‘std::basic_istream<char>::basic_istream(const std::basic_istream<char>&)’ is implicitly deleted because the default definition would be ill-formed: /usr/include/c++/4.6/istream:57:11: error: use of deleted function ‘std::basic_ios<char>::basic_ios(const std::basic_ios<char>&)’ [SNIP] 

... This goes on for several pages.

What's wrong with my code?

Update 1: Clang 3.0 fails with similar results.
Update 2: g++ 4.7 fails also.
Update 3: Using the answers as a guide, I found this: c++11 status in libstdc++ - "27.5 Iostreams base classes: Missing move and swap operations on basic_ios." Curses!

like image 649
x-x Avatar asked Aug 18 '12 04:08

x-x


People also ask

What is the purpose of std :: move?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

When should I use move semantics?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

Do I need to return std :: move?

std::move is totally unnecessary when returning from a function, and really gets into the realm of you -- the programmer -- trying to babysit things that you should leave to the compiler.

Can you std :: move a reference?

"std::move" should only be used where moving can happenWhen passing the result of std::move as a const reference argument. In this case, no object will be moved since it's impossible to call the move constructor from within the function.


1 Answers

Update

It is required to work according to the C++11/14 standards. GCC 5.0 gets it right, and the below mentioned bug is RESOLVED. Thank you GCC team!

Original answer

It's a missing feature in gcc (or as Xeo points out libstdc++) as of yet.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316

According to the standard,

typedef basic_stringstream<char> stringstream; 

and from 27.8.5, there is a public move constructor

basic_stringstream(basic_stringstream&& rhs); 

I can confirm the problem with gcc 4.7 -std=c++11, ubuntu 12.04.

std::stringstream a; std::stringstream b=std::move(a); 

reading the include file include/std/sstream I find no move constructor or any mentioning of C++0x or C++11. (compare with std::string which does work.)

Adding a (mock) move constructor:

basic_stringstream(basic_stringstream&& rhs){} 

reduces the error to only tree lines, but

use of deleted function ‘std::basic_stringstream<char>::basic_stringstream(    const std::basic_stringstream<char>&)’ 

remains.

Workaround

Use a std::unique_ptr<std::stringstream> instead. Best is to initialize it with make_unique which comes with c++14, or take it for example from my blog cpp11style-no-new-delete (that is an earlier version, but it will work fine for this purpose).

like image 183
Johan Lundberg Avatar answered Oct 26 '22 09:10

Johan Lundberg