传递一个rvalue引用并将其保留在周围是被破坏的,并且几乎肯定会导致沿途的未定义行为(UB)。我指的是以下代码,它有助于但不直接导致UB:
IOLoader(std::istream&& t_stream) : stream_(t_stream)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};// get rid of the header
构造函数使以下行可以无提示地触发UB:
for(IOLoader data1_loader = IOLoader((std::stringstream(data1))); !data1_loader.IsEnd(); data1_loader.Next())
这一行创建一个临时(rvalue)
stringstream
IOLoader
接受rvalue引用的。但是接受rvalue引用的构造函数不移动任何内容,只存储对临时
. 这与通常使用rvalue引用(即移动对象)相反。当循环体开始时
弦流
已经被摧毁了,而且
stream_
Next()
,或者以任何其他方式,是UB。
您可以通过创建
stingsstream
对象:
std::stringstream tmp_stream(data1);
for(IOLoader data1_loader = IOLoader(tmp_stream); !data1_loader.IsEnd(); data1_loader.Next())
这将修复实例,但不会修复核心问题。核心问题是误导的存在。
&&
&&
构造函数,或者将其全部移除,或者使其实际移动
弦流
class IOLoader
{
...
IOLoader(std::stringstream&& t_stream) : saved_stream_(std::move(t_stream)), stream_(saved_stream_)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};// get rid of the header
...
std::stringstream saved_stream_;
std::istream& stream_;
std::string line_;
};
缺点是在这种情况下,它只能与
弦流
istringstream
. 通过使用模板,可以使其更通用(附加堆分配的运行时成本):
class IOLoader
{
public:
....
// enable_if avoids regular references, so that we neither prefer this ctor
// over the other ctor, nor try to move from a regular lvalue reference.
template <typename Stream, typename = typename std::enable_if<!std::is_reference<Stream>::value>::type>
IOLoader(Stream&& t_stream) : saved_stream_(std::make_unique<typename std::decay<Stream>::type>(std::move(t_stream))), stream_(*saved_stream_)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};
...
std::unique_ptr<std::istream> saved_stream_;
std::istream& stream_;
std::string line_;
};