【问题标题】:Returning an fstream返回一个 fstream
【发布时间】:2014-06-04 06:56:21
【问题描述】:

我有这个功能:

fstream open_user_file() const
{
    ...
}

但我的编译器抱怨 fstream 复制构造函数被隐式删除。既然编译器执行 RVO,为什么选择复制构造函数而不是移动构造函数?

否则,最好的方法是什么?

【问题讨论】:

  • RVO 要求类型是可复制的。
  • @0x499602D2 有什么原因吗?
  • 不确定,但标准要求复制构造函数可以访问,即使它会被优化掉。

标签: c++ fstream move-semantics rvo


【解决方案1】:

当前接受的答案是错误的。

当返回一个自动存储的局部变量,与函数声明的返回类型相同时,会有一个两阶段的过程:

fstream open_user_file() const
{
    fstream f;

    /*...*/

    return f;
}
  1. 首先执行复制构造函数的选择,就好像对象由右值指定一样。

  2. 如果第一次重载决议失败或未执行,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。

这意味着如果f 是可移动构造的,则返回f 将是首选(并且可能被省略)。否则,如果f 是可复制构造的,则将完成(并且可能省略)返回f。否则 f 无法从此函数返回,并会导致编译时错误。

唯一的情况是:

return std::move(f);

应该在实现有问题时提供帮助。在符合要求的实现中,fstream 是可移动构造的,并且:

return f;

将是最佳的。如果f 不可移动构造,则:

return std::move(f);

对符合标准的实现没有帮助。而且,如果在符合规范的实现中无论如何编码都会产生悲观化的效果,因为它会抑制 RVO。

gcc 4.8 尚未实现可移动流(并且流不可复制)。这就是你问题的根源。在 C++98、C++03 和 gcc 4.8 中,流不能从函数返回。在 C++11 中它们是。

【讨论】:

  • bpaste.net/show/222137 这段代码不能用 Clang 3.4 编译,会不会遇到同样的问题?
  • @qdii:如果您安装的 clang 3.4 使用的是 gcc 的 libstdc++,那么可以。但是,如果 clang 使用的是 libc++ (libcxx.llvm.org),那么上面的将编译。
【解决方案2】:

即使复制构造函数有副作用,实现也可以省略由 return 语句产生的复制操作。在这种情况下,您可能只需要显式移动。

fstream open_user_file() const
{
    fstream f;

    /*...*/

    return std::move(f);
}

当满足某些条件时,允许实现省略类的复制/移动构造 对象,即使为复制/移动操作选择了构造函数和/或对象的析构函数 有副作用。

...

这就是它说复制构造函数必须是可访问的:

当满足省略复制操作的条件时 object是函数参数,要复制的对象由左值指定,重载解析为 选择复制的构造函数首先执行,就好像对象是由右值指定的。如果过载 解析失败,或者如果所选构造函数的第一个参数的类型不是对的右值引用 对象的类型(可能是 cv 限定的),再次执行重载决议,将对象视为 左值。 [注:无论是否复制省略,都必须执行此两阶段重载解析 发生。它确定如果不执行省略则要调用的构造函数,以及选择的构造函数 即使调用被省略,也必须可以访问。

【讨论】:

  • 请注意,GCC 4.8 尚未实现可移动文件流。不过,这将在 Clang 3.4 中编译。
猜你喜欢
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-27
  • 1970-01-01
相关资源
最近更新 更多