如何在C++中将不可复制的局部变量移出lambda?

aby*_*s.7 2 c++ lambda move-semantics c++11

我想实现一个简单的运行时检查宏,所以它的工作方式如下:

CHECK(expr) << "Some problem!";
Run Code Online (Sandbox Code Playgroud)

我写了一个简化的日志记录类来做到这一点:

class Log {
 public:
  Log() = default;
  Log(const Log&) = delete;

  ~Log() {
    cout << this << " dtor" << endl;
    cout << stream_.str() << endl;
  }

  template <class T>
  Log& operator<<(const T& info) {
    cout << this << " <<" << endl;
    stream_ << info;
    return *this;
  }

 private:
  stringstream stream_;
};
Run Code Online (Sandbox Code Playgroud)

让宏观:

#define CHECK(expr) \
  if (!(expr)) [] { /* See attempts below */ }()
Run Code Online (Sandbox Code Playgroud)

现在让我们尝试实现lambda.


尝试#1

最简单的方法应该是:

[] {
  Log log;
  log << "A";
  return log;
}
Run Code Online (Sandbox Code Playgroud)

没有运气 - 复制构造函数被删除:

error: use of deleted function 'Log::Log(const Log&)'
Run Code Online (Sandbox Code Playgroud)

尝试#2

好吧,让我们明确地移动局部变量:

[] {
  Log log;
  log << "A";
  return move(log);
}
Run Code Online (Sandbox Code Playgroud)

嗯,还是没有运气.

尝试#3

一种绝望的尝试当然不应该起作用:

[]() -> Log&& {
  Log log;
  log << "A";
  return move(log);
}
Run Code Online (Sandbox Code Playgroud)

它编译甚至运行,但是operator <<在析构函数之后调用:

0xbfe84064 dtor
A
0xbfe84064 <<
Run Code Online (Sandbox Code Playgroud)

帮我弄清楚从lambda返回一个变量我做错了什么?

vso*_*tco 5

制作移动构造函数default,即

Log(Log&&) = default;
Run Code Online (Sandbox Code Playgroud)

因为否则存在用户提供的复制ctor(即使deleted)禁用移动ctor.您也应该return log;代替return move(log);,因为默认移动ctor将被调用(因为复制ctor被删除).例如参见关于为什么越来越多的信息return move一般应避免.

Live on Coliru