Подтвердить что ты не робот

Почему семантика перемещения для класса, содержащего std:: stringstream, вызывая ошибки компилятора?

Как я могу сделать этот простой класс подвижным? То, что я считал правильным, просто создает стену ошибок...

#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

... и получить стену ошибок о назначениях копий, которые вызывают для различных базовых классов строкового потока.

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]

... Это продолжается для нескольких страниц.

Что не так с моим кодом?

Обновление 1: Clang 3.0 не работает с аналогичными результатами.
Обновление 2: g++ 4.7 также не работает.
Обновление 3. Используя ответы в качестве руководства, я нашел следующее: статус С++ 11 в libstdС++ - "27.5. Базовые классы Iostreams: Отсутствующие перемещать и свопировать операции на basic_ios". Проклятия!

4b9b3361

Ответ 1

Update

Требуется работать в соответствии со стандартами С++ 11/14. GCC 5.0 исправляет это, и указанная ниже ошибка RESOLVED. Спасибо команде GCC!

Оригинальный ответ

Это недостающая функция в gcc (или как Xeo указывает libstdС++).

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

Согласно стандарту,

typedef basic_stringstream<char> stringstream;

и из 27.8.5 существует конструктор общедоступного перемещения

basic_stringstream(basic_stringstream&& rhs);

Я могу подтвердить проблему с помощью gcc 4.7 -std=c++11, ubuntu 12.04.

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

чтение файла include include/std/sstream Я не нахожу конструктор перемещения или никакого упоминания о С++ 0x или С++ 11. (сравните с std::string, который работает.)

Добавление конструктора перемещения (mock):

basic_stringstream(basic_stringstream&& rhs){}

сводит ошибку только к строкам дерева, но

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

остается.

Обход

Вместо этого используйте std::unique_ptr<std::stringstream>. Лучше всего инициализировать его с помощью make_unique, который поставляется с С++ 14, или взять его, например, из моего блога cpp11style-no-new-delete (то есть более ранняя версия, но с этой целью она будет работать нормально).

Ответ 2

Clang 3.0 - это С++ 98, Clang 3.1 компилирует это просто отлично (с libС++):

~/blargh $ cat t.cpp 
#include <sstream>

int main(){
  auto get_s = []{ return std::stringstream("hi"); };
  auto s = get_s();
}
~/blargh $ clang -v
clang version 3.1 (trunk 152621)
Target: x86_64-unknown-linux-gnu
Thread model: posix
~/blargh $ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp 
~/blargh $

Ваш пример тоже очень хорош, поэтому я предполагаю, что это libstdС++, который сломан в этом отношении.

Ответ 3

Вероятно, вы используете плохую версию библиотеки. Нет причин, по которым ваш код должен быть недействительным.

Кстати, операторы присваивания для самонаведения плохо работают.

Edit: Любопытно, что Visual Studio также, похоже, считает, что basic_stringstream не имеет конструктора перемещения. Все потоки должны перемещаться в С++ 11. Возможно, это стандартный дефект, или для реализации конструктора move требуется, чтобы функция С++ 11 еще не поддерживала. Понятно, что нет причин, по которым это должно потерпеть неудачу, но я не могу найти ничего, чтобы поддержать это.