依靠确定性破坏,避免返回时的破坏

时间:2017-07-22 12:43:31

标签: c++ c++14

我一直在尝试修改我的日志记录课程。但是,我遇到了一个问题 我想向用户公开这个界面:

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。编译器总是执行copy-elision,因此析构函数确实按照我想要的方式运行。但是,使用这段代码,换行字符将被打印两次,因为复制构造函数是在operator()中复制时调用的。
当我不返回临时实例时,问题就消失了,而是把它放在operator()的正文中:

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

现在RVO让它按照我想要的方式工作 我稍后在= delete上复制构造函数,因为它更有意义,这有效地导致代码无法编译。

我有什么选择来提供我想要的界面,而不使用hacky解决方案来依赖RVO?

1 个答案:

答案 0 :(得分:2)

LogStream添加char const *的构造函数。

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

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

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

因此避免了临时性以及额外的新行。

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