【问题标题】:Force use of copy constructor / Avoid use of copy constructor强制使用复制构造函数/避免使用复制构造函数
【发布时间】:2016-04-06 09:32:30
【问题描述】:

我目前正在编写一个日志类(只是为了练习)并遇到了一个问题。我有两个类: Buffer 类充当临时缓冲区,并在其析构函数中刷新自身。还有返回Buffer实例的Proxy类,不用一直写Buffer()。

不管怎样,代码如下:

#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>

class Buffer
{
private:
  std::stringstream buf;
public:
  Buffer(){};
  template <typename T>
  Buffer(const T& v)
  {
    buf << v;
    std::cout << "Constructor called\n";
  };
  ~Buffer()
  {
    std::cout << "HEADER: " << buf.str() << "\n";
  }
  Buffer(const Buffer& b)
  {
    std::cout << "Copy-constructor called\n";
    // How to get rid of this?
  };
  Buffer(Buffer&&) = default;
  Buffer& operator=(const Buffer&) & = delete;
  Buffer& operator=(Buffer&&) & = delete;
  template <typename T>
  Buffer& operator<<(const T& v)
  {
    buf << v;
    return *this;
  }
};

class Proxy
{
public:
  Proxy(){};
  ~Proxy(){};
  Proxy(const Proxy&) = delete;
  Proxy(Proxy&&) = delete;
  Proxy& operator=(const Proxy&) & = delete;
  Proxy& operator=(Proxy&&) & = delete;
  template <typename T>
  Buffer operator<<(const T& v) const
  {
    if(v < 0)
      return Buffer();
    else
      return Buffer(v);
  }
};

int main () {  
  Buffer(Buffer() << "Test") << "what";
  Buffer() << "This " << "works " << "fine";
  const Proxy pr;
  pr << "This " << "doesn't " << "use the copy-constructor";
  pr << "This is a " << std::setw(10) << " test";
  return 0;
}

这是输出:

Copy-constructor called
HEADER: what
HEADER: Test
HEADER: This works fine
Constructor called
HEADER: This doesn't use the copy-constructor
Constructor called
HEADER: This is a       test

代码完全符合我的要求,但它取决于 RVO。我多次读到您不应该依赖 RVO,所以我想问一下我该怎么做:

  1. 完全避免使用 RVO,以便每次都调用复制构造函数
  2. 避免使用复制构造函数

我已经尝试通过返回引用或移动来避免复制构造函数,但会出现段错误。我猜那是因为 Proxy::operator

我也对功能大致相同的完全不同的方法感兴趣。

【问题讨论】:

  • 3.编写不依赖于 RVO 是否发生的代码。
  • 我想这是显而易见的选择。但我想避免一个很可能永远不会被调用的复制构造函数。
  • 无论如何,您的课程不应该是可复制或可分配的(它有一个 stringstream 数据成员。)我真的不知道您要在这里解决什么问题。

标签: c++ copy-constructor rvo


【解决方案1】:

这似乎是一个人为的问题:首先,无论 RVO 是启用还是禁用,代码都可以正常工作(您可以使用 G++ 和 the no-elide-constructors flag 对其进行测试)。其次,您设计与&lt;&lt; 运算符一起使用的Buffer 对象的返回方式只能通过复制 来完成:Proxy::operator&lt;&lt;(const T&amp; v) 函数创建一个新的Buffer 实例在堆栈上,然后在您离开函数调用时将其删除(即在pr &lt;&lt; "This " &lt;&lt; "doesn't " &lt;&lt; "use the copy-constructor"; 中的每个串联之间);这就是为什么在尝试从函数外部引用此对象时会出现分段错误的原因。

或者,您可以定义一个 &lt;&lt; 运算符来使用动态内存,例如返回一个unique_ptr&lt;Buffer&gt;:

#include <memory>

...

std::unique_ptr<Buffer> operator<<(const T& v) const
{
    if(v < 0)
        return std::unique_ptr<Buffer>(new Buffer());
    else
        return std::unique_ptr<Buffer>(new Buffer(v));
}

但是,您的原始连接语句将无法编译,因为 Proxy::operator&lt;&lt;(const T&amp; v) 现在返回类型为 std::unique_ptr&lt;Buffer&gt; 而不是 Buffer 的对象,这意味着该返回的对象没有自己的 Proxy::operator&lt;&lt;(const T&amp; v)定义了函数,因此如果不首先显式取消引用返回的指针,多个连接将无法工作:

const Proxy pr;
std::unique_ptr<Buffer> pb = pr << "This ";
//  pb << "doesn't " << "use the copy-constructor"; // This line doesn't work
*pb << "doesn't " << "use the copy-constructor";

换句话说,您的类本质上依赖于复制,因此,如果您真的想避免复制,您应该丢弃它们并完全重新设计您的日志记录功能。


我敢肯定有一些黑魔法巫术可以被调用来使这成为可能——尽管以一个人的理智为代价。

【讨论】:

  • 谢谢!这总结得很好。我想我会坚持复制然后
猜你喜欢
  • 1970-01-01
  • 2013-10-13
  • 1970-01-01
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-06
相关资源
最近更新 更多