2

I want to implement a simple runtime check macro, so it works like:

CHECK(expr) << "Some problem!";

I wrote a simplified logging class to do so:

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_;
};

And let the macro be:

#define CHECK(expr) \
  if (!(expr)) [] { /* See attempts below */ }()

Now let's try to implement the lambda.


Attempt #1

The simplest way should be:

[] {
  Log log;
  log << "A";
  return log;
}

But no luck - the copy constructor is deleted:

error: use of deleted function 'Log::Log(const Log&)'

Attempt #2

Ok, let's move the local variable explicitly:

[] {
  Log log;
  log << "A";
  return move(log);
}

Hmm, still no luck.

Attempt #3

A desperate attempt that certainly shouldn't work:

[]() -> Log&& {
  Log log;
  log << "A";
  return move(log);
}

It compiles and even runs, but the operator << is called after destructor:

0xbfe84064 dtor
A
0xbfe84064 <<

Help me to figure out what am I doing wrong with returning a variable from lambda?

abyss.7
  • 13,882
  • 11
  • 56
  • 100
  • I'd really avoid return local variable from function (including lambda expression). In your case - just `#define CHECK(e) if (!(e)) Log{} << "A"`. Even better for some other reason `#define CHECK(e) if (e) {} else Log{} << "A"` – PiotrNycz Jan 24 '16 at 18:32
  • @PiotrNycz It's not a good variant for me, since I want to print a stack trace along with a user-provided message - that's why I use `Log` inside lambda to do some stuff. – abyss.7 Jan 24 '16 at 18:40
  • Not a problem at all. You can create object that prints stack track - like `Log{} << stackTracePrinter`. Or you can do everything you can imagine in some Log specialization constructor... – PiotrNycz Jan 24 '16 at 19:57

1 Answers1

5

Make the move constructor default, i.e.

Log(Log&&) = default;

because otherwise the existence of a user-provided copy ctor (even if deleted) disables the move ctor. You should also return log; instead of return move(log);, as the default move ctor will be invoked (since the copy ctor is deleted). See e.g. this for more details on why return move should in general be avoided.

Live on Coliru

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252