我目前正在编写一个日志记录类(仅用于练习)并遇到了一个问题。我有两个类: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所以我想问我怎么做:
我已经尝试通过返回引用或移动但是段错误来避免复制构造函数。我猜那是因为Proxy :: operator&lt;&lt;在退货期间被删除。
我也对大致相同的完全不同的方法感兴趣。
答案 0 :(得分:1)
这似乎是一个人为的问题:首先,无论是启用还是禁用RVO,代码都能正常运行(您可以使用带有the no-elide-constructors
flag的G ++来测试它。其次,设计返回Buffer
对象以便与<<
运算符一起使用的方式只能通过复制†来完成:Proxy::operator<<(const T& v)
函数创建堆栈上的新Buffer
实例,当您离开函数调用时(即pr << "This " << "doesn't " << "use the copy-constructor";
中的每个串联之间),该实例将被删除;这就是在尝试从函数外部引用此对象时出现分段错误的原因。
或者,您可以定义<<
运算符以使用动态内存,例如:返回unique_ptr<Buffer>
:
#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<<(const T& v)
现在返回类型为std::unique_ptr<Buffer>
的对象而不是Buffer
,这意味着此返回的对象不会定义了自己的Proxy::operator<<(const T& 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";
换句话说,你的类本身就依赖于复制,因此,如果你真的想避免复制,你应该抛弃它们并完全重新设计你的日志功能。
†我确信有一些黑魔法伏都教可以用来实现这一点 - 虽然这是以一个人的理智为代价的。 功能