Почему семантика перемещения для класса, содержащего 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". Проклятия!
Ответы
Ответ 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 еще не поддерживала. Понятно, что нет причин, по которым это должно потерпеть неудачу, но я не могу найти ничего, чтобы поддержать это.