在没有移动构造函数的情况下返回不可复制对象的解决方法
Workaround for returning uncopyable object without a move ctor
在我的 API 我有一个函数 returns std::istringstream
.
std::istringstream
class 是不可复制的,但支持移动,因此在符合标准的编译器上返回本地 std::istringstream
.
没有问题
但是,在 gcc 4.9 上,有 no support 用于移动 std::istringstream
。
是否有一些解决方法可以让我使用 std::istringstream
而无需从用户的角度更改 API?
建议 here 的解决方法是使用 unique_ptr<std::istringstream>
将更改 API 的语义。
如果你不能移动std::istringstream
,那也没办法了。
如果一个对象是不可复制和不可移动的,你不能return它的值。
如果你想支持新功能,你最好为这些新的编译器。
在用餐时间,你可以return一个unique_ptr
。如果您真的渴望按值 return,您可以 return 一个包含 std::unique_ptr<std::istringstream>
的可移动包装器并提供与 istringstream 相同的接口。但是,这也会影响 return 类型。
通过右值引用 return 可能很诱人。您可以执行以下操作:
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 构造函数的情况下 return class 的方法是使用带括号初始化列表的 return 语句:
class C {
C() = default;
C(const C&) = delete;
C(C&&) = delete;
};
C make_C() { return {}; }
int main() {
C&& c = make_C();
}
不幸的是,此初始化仅考虑非显式构造函数并且 std::istringstream
具有显式构造函数。
一个解决方法是创建一个子 class 具有非显式构造函数:
struct myIStringStream : std::istringstream
{
myIStringStream () = default;
};
myIStringStream make_istringstream()
{
return {};
}
int main()
{
std::istringstream&& iss = make_istringstream();
}
为完整性和未来参考回答我自己的问题。
目标是为 gcc (< 5) 错误找到 解决方法 ,其中 std::istringstream
不提供在我需要的情况下可以工作的移动构造函数到 return 不可复制和(bugly-)不可移动的流。
正如评论中提到的,我实际上可以将我的函数签名(至少在 gcc < 5 上)更改为 return 一个代理对象,允许复制或移动而不更改 API 上使用的代码 newer/other 编译器。
同事建议并实施的想法是围绕 std::istringstream
创建一个代理对象,它提供了一个类似的 API,但也提供了一个 copy-ctor,它手动创建和初始化一个来自复制自流的新内部 std::istringstream
。此代理仅用于有问题的编译器。
其自然栖息地的代码是here。
这是相关部分:
#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
在我的 API 我有一个函数 returns std::istringstream
.
std::istringstream
class 是不可复制的,但支持移动,因此在符合标准的编译器上返回本地 std::istringstream
.
但是,在 gcc 4.9 上,有 no support 用于移动 std::istringstream
。
是否有一些解决方法可以让我使用 std::istringstream
而无需从用户的角度更改 API?
建议 here 的解决方法是使用 unique_ptr<std::istringstream>
将更改 API 的语义。
如果你不能移动std::istringstream
,那也没办法了。
如果一个对象是不可复制和不可移动的,你不能return它的值。 如果你想支持新功能,你最好为这些新的编译器。
在用餐时间,你可以return一个unique_ptr
。如果您真的渴望按值 return,您可以 return 一个包含 std::unique_ptr<std::istringstream>
的可移动包装器并提供与 istringstream 相同的接口。但是,这也会影响 return 类型。
通过右值引用 return 可能很诱人。您可以执行以下操作:
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 构造函数的情况下 return class 的方法是使用带括号初始化列表的 return 语句:
class C {
C() = default;
C(const C&) = delete;
C(C&&) = delete;
};
C make_C() { return {}; }
int main() {
C&& c = make_C();
}
不幸的是,此初始化仅考虑非显式构造函数并且 std::istringstream
具有显式构造函数。
一个解决方法是创建一个子 class 具有非显式构造函数:
struct myIStringStream : std::istringstream
{
myIStringStream () = default;
};
myIStringStream make_istringstream()
{
return {};
}
int main()
{
std::istringstream&& iss = make_istringstream();
}
为完整性和未来参考回答我自己的问题。
目标是为 gcc (< 5) 错误找到 解决方法 ,其中 std::istringstream
不提供在我需要的情况下可以工作的移动构造函数到 return 不可复制和(bugly-)不可移动的流。
正如评论中提到的,我实际上可以将我的函数签名(至少在 gcc < 5 上)更改为 return 一个代理对象,允许复制或移动而不更改 API 上使用的代码 newer/other 编译器。
同事建议并实施的想法是围绕 std::istringstream
创建一个代理对象,它提供了一个类似的 API,但也提供了一个 copy-ctor,它手动创建和初始化一个来自复制自流的新内部 std::istringstream
。此代理仅用于有问题的编译器。
其自然栖息地的代码是here。
这是相关部分:
#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