Как заставить "фабричную функцию" возвращать не копируемый объект?
контекст
Пытаясь создать какой-нибудь архив gzip с другим именем внутри, я написал следующий фрагмент кода.
#include <iostream>
#include <utility>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filter/gzip.hpp>
boost::iostreams::filtering_ostream&& makeGZipStream(const std::string& archiveName,
const std::string& fileName)
{
boost::iostreams::filtering_ostream theGzipStream;
boost::iostreams::gzip_params theGzipParams;
theGzipParams.file_name = fileName;
theGzipStream.push(boost::iostreams::gzip_compressor{theGzipParams});
theGzipStream.push(boost::iostreams::file_sink{archiveName});
return std::move(theGzipStream);
}
int main()
{
boost::iostreams::filtering_ostream&& theGzipStream = makeGZipStream("archive.gz", "file");
theGzipStream << "This is a test..." << std::endl;
return 0;
}
проблема
Это (как мы можем ожидать) создает дамп ядра, потому что в makeGZipStream
мы пытаемся вернуть локальную переменную, выделенную стеком, по ссылке (rvalue-). Но копия не была вариантом в этом случае, так как boost::iostreams::filtering_ostream
не подлежит копированию.
Вопросы
- Мы можем вернуть
std::unique_ptr
"по значению" благодаря конструктору перемещения (а перемещение не должно происходить даже в C++17 благодаря разрешению копирования), почему это невозможно в этом случае? - Какие там хорошие решения?
Возможные решения
- Поместить все в одну область (чего я пытался избежать)
- Обернуть ваш объект в
unique_ptr
(не так красиво) - Что-нибудь еще?
Заметки
Компилятор был довольно старый g++ (GCC) 4.9.3
,
1 ответ
Решение
Просто верните по значению и удалите std::move()
из декларации возврата:
boost::iostreams::filtering_ostream makeGZipStream(const std::string& archiveName,
const std::string& fileName)
{
...
return theGzipStream;
}
если он не скомпилируется из-за отсутствующего конструктора перемещения, то вам не повезло, и этот тип не может быть перемещен, поэтому поместите его в std::unique_ptr
и вернуть этот указатель по значению (которое является подвижным наверняка).