c++ - 依靠确定性销毁,避免返回时销毁

标签 c++ c++14

我一直在尝试重新设计我的日志记录类。但是,我遇到了一个问题。
我想向用户公开这个接口(interface):

mylog() << "hello";

这个想法是,mylogLogger 的一个实例,它为已定义的日志类型定义了一些有用的特征。它的 operator() 函数将返回一个 LogStream 类型的实例。但是,我希望在末尾自动输出换行符,因此我想到在 LogStream 的析构函数中执行此操作。

我当前的实现如下所示(LogStreamLogger 很大程度上被简化了):

#include <iostream>

struct LogStream
{
    ~LogStream() { std::cout << '\n'; }

    template<class T>
    LogStream& operator<<(const T& t)
    {
        std::cout << t;
        return *this;
    }
};

struct Logger
{
    LogStream operator()()
    {
        return LogStream{} << "message: ";
    }
};

int main()
{
    Logger log;
    log() << "hello!";
}

有趣的是,我通过这段代码发现我之前的实现依赖于 RVO。编译器始终执行复制省略,因此析构函数确实按照我想要的方式运行。然而,对于这段代码,换行符被打印两次,因为当复制发生在 operator() 中时,复制构造函数被调用。
当我不返回临时实例,而是将其放入 operator() 的主体中时,问题就消失了:

LogStream stream;
stream << "message: ";
return stream;

现在 RVO 让它按照我想要的方式工作。
后来我=delete删除了复制构造函数,因为无论如何它更有意义,这实际上导致代码无法编译。

我可以选择哪些选项来提供我想要的界面,而不使用依赖 RVO 的 hacky 解决方案?

最佳答案

LogStream 添加一个构造函数,该构造函数采用 char const *

LogStream(char const* c) { std::cout << c; }

然后,不要在 operator() 中创建临时 LogStream,而是使用列表初始化来初始化返回值本身。

LogStream operator()()
{
    return {"message: "};
}

因此,可以避免临时值以及额外的新行。

Live demo (请注意,即使使用 -fno-elide-constructors 禁用复制省略也不会导致额外的换行符)。

关于c++ - 依靠确定性销毁,避免返回时销毁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45254710/

相关文章:

java - 将 C++ OpenGL 帧缓冲区代码移植到 LWJGL - 我使用什么类?

c++ - 如何使用 const/nonconst 指针/引用参数模板化函数

c++ - 具有由整数确定的固定数量参数的函数

c++ - 乘以可变数量的参数

c++ - 使用 -std=c++11 后未在此范围内声明“stoi”

c++ - 测试数组中的值

c++ - 结构数组似乎不想工作

c++ - Fortran ifstream 等价物

C++ 模板在模板函数实例化时类型未知