在没有移动函数的情况下返回不可复制对象的解决方法
Workaround for returning uncopyable object without a move ctor
在我的API中,我有一个返回std::istringstream
的函数。std::istringstream
类是不可复制的,但支持在符合标准的编译器上移动,因此返回本地std::istringstream
没有问题。
但是,在gcc 4.9中,不支持移动std::istringstream
。是否有一些变通办法,我可以使用std::istringstream
,而不改变API从用户的角度?
此处建议使用unique_ptr<std::istringstream>
的解决方案将改变API的语义。
如果你不能移动std::istringstream
,那就没有什么办法了。
如果一个对象是不可复制和不可移动的,你不能按值返回它。如果你想要支持新特性,你最好为这些特性买一个新的编译器。
在此期间,您可以返回unique_ptr
。如果您真的希望按值返回,您可以返回一个包含std::unique_ptr<std::istringstream>
的可移动包装器,并提供与istringstream相同的接口。但是,这也会影响返回类型。
通过右值引用返回可能很诱人。你可以这样做:
struct MyApiClass {
std::istringstream&& get_stream() {
return std::move(*_stream);
}
private:
std::unique_ptr<std::istringstream> _stream;
};
然后,在旧的编译器中,你可以这样使用它:
std::istringstream&& stream = myApiClass.get_stream();
// use stream as long as myApiClass exists
使用新编译器的人可以这样使用它:
std::istringstream stream = myApiClass.get_stream();
// use stream normally
这是api受影响较小的方式。除此之外,我不知道有什么办法。
不使用move/copy构造函数返回class的方法是使用带大括号init-list的返回语句:
class C {
C() = default;
C(const C&) = delete;
C(C&&) = delete;
};
C make_C() { return {}; }
int main() {
C&& c = make_C();
}
演示不幸的是,这个初始化只考虑非显式构造函数,而std::istringstream
有显式构造函数。
struct myIStringStream : std::istringstream
{
myIStringStream () = default;
};
myIStringStream make_istringstream()
{
return {};
}
int main()
{
std::istringstream&& iss = make_istringstream();
}
演示回答我自己的问题,以供完整和将来参考。
我们的目标是为gcc (<5) std::istringstream
不提供移动函数的bug,在我想返回不可复制和(bug -)不可移动流的情况下,该函数将起作用。
正如在评论中提到的,我实际上可以改变我的函数签名(至少在gcc <5)返回一个代理对象,允许在不改变API的情况下复制或移动新编译器/其他编译器上使用的代码。
这个想法,由一个同事建议和实现,是在std::istringstream
周围创建一个代理对象,它提供了类似的API,但也提供了一个复制-ctor,它从复制-from流手动创建和初始化一个新的内部std::istringstream
。这个代理只在有问题的编译器上使用。
其自然栖息地的代码在这里。
以下是相关部分:
#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
- C++17 如何保存泛型可调用对象以供以后使用
- 将包含不可复制对象的对插入到映射中
- 通用参考 l 值不复制对象
- 简单可复制与可简单复制
- 可变参数宏:无法通过"..."传递非平凡可复制类型的对象
- C :对象上的可复制视图
- 提升::可选与标准::不可复制对象的可选
- 错误:无法通过'...'传递非平凡可复制类型的对象'class boost::filesystem::path'
- 使用realloc可以安全地重新分配琐碎的可复制对象的存储吗
- 传递可选对象而无需复制它
- "constructing"一个带有memcpy的可复制对象
- 错误:无法通过`..`传递非普通可复制类型的对象
- 如何初始化非默认可构造不可复制对象的元组
- C++11/VS2010:返回包含不可复制但可移动对象的容器
- 无法传递非普通可复制类型“const class mysqlpp::String”的对象
- 将std::memcpy用于非平凡可复制类型的对象
- 如何返回一个不可移动(但可复制)的对象
- 正在复制通常在c++ 14中定义的可复制对象
- std::vector emplace_back() 表示非复制可构造对象
- 作用于可移动但不可复制对象序列的变化STL算法的行为