#include <iostream>
using namespace std;

template<typename Fn>
void call_value(Fn f) { f(); }
template<typename Fn>
void call_ref(Fn & f) { f(); }
template<typename Fn>
void call_cref(Fn const & f) { f(); }

struct Data {
  Data() {}
  Data(Data const &) {
    cout << "copy" << endl;
  }
  Data(Data &&) {
    cout << "move" << endl;
  }
};

int main(int, char **) {
  Data data;

  auto capref = [&data] () {};
  cout << "capture by value, so we get a ";
  auto capcp = [data] () {};

  cout << " the lambda with a reference ... ";
  call_value(capref);
  cout << " could now be called and mutate .. ";
  call_ref(capref);
  call_cref(capref);
  cout << " but won't, as it had to be declared mutable " << endl;

  cout << "the lambda with an instance: ";
  call_value(capcp);
  cout << "but not ";
  call_ref(capcp);
  call_cref(capcp);
  cout << " the reference versions " << endl;

  bool en = false;
  auto trigger = [en](bool enable = true) mutable {
    if (en) {
      cout << "fire!" << endl;
    }
    if (en or enable) {
      en = true;
    }
  };
  cout << "won't shoot" << endl;
  trigger(false);
  call_value(trigger);
  trigger(false);
  call_ref(trigger);
  cout << "and now ... ";
  trigger(false);
  // const ref won't work
  return 0;
}