Search code examples
c++visual-studio-2010c++11lambdafrp

lambdas and their captured values


I've just been doing some experimenting recently with functional reactive programming and what it is, and when i was trying to implement something like it in c++, i turned to the lambda for some help. I came up with the beginnings of something like this,

template<class T>
class Reactive
{ 
public:
  Reactive(T data)
  {
      m_Data = data;
  }

  Reactive(std::function<T()> func, T data)
  {
    m_Data = data;
    m_Affecter = func;
  }

  template<class H>
  Reactive & operator+(const H & rhs)
  {
      m_Data += rhs;

      return *this;
  }

  template<class H>
  Reactive operator+(Reactive<H> & rhs)
  {
    std::function<decltype(m_Data + rhs.m_Data)()> func;

    if (!rhs.m_Affecter)
      func = [&](){return m_Data + rhs.m_Data;};
    else
      func = [&](){return m_Data + rhs.m_Affecter();};

    return Reactive<decltype(m_Data + rhs.m_Data)> (func, m_Data + rhs.m_Data);
  }

  Reactive & operator=(const T & data)
  {
      m_Data = data;

      return *this;
  }

  Reactive & operator=(const Reactive & rhs)
  {
      m_Data = rhs.m_Data;
      m_Affecter = rhs.m_Affecter;

      return *this;
  }

  T & Get()
  {
      return m_Data;
  }

  void Update()
  {
      m_Data = m_Affecter();
  }

private:
  std::function<T()> m_Affecter;
  T m_Data;
};

It only support addition thus far. i was trying to make an object called a reactive that can wrap around any other type, except that when a math operation is performed on it, a lambda is created, where that operation takes place, so as to kind of remember what has been done to it before, and to do it again when one of it's affecting values has changed, (after calling the update function). for example if i were to do this.

Reactive<int> cheh = 0;
Reactive<int> meh = 3;
Reactive<int> peh = 7;
cheh = meh + peh;
meh = meh + 4;
cheh.Update();
std::cout << cheh.Get();

then heres what would take place. summing the two Reactives in line five would make another Reactive with their two values added to 10 as well as set it's affector to a lambda that does something like this, &{meh.m_Data + peh.m_Data} . Then that Reactive would be assigned to cheh. When the value m_meh had 4 added to it, and cheh was updated, it's affector was called with the new value of 14 and that is what is printed to the screen, exactly as i'd intended.

But then i got to thinking, what if one of those Reactives that takes part in cheh's affecter goes out of scope. The program should error out, if not handled correctly. So i did this,

Reactive<int> cheh = 0;
Reactive<int> meh = 3;

{
    Reactive<int> peh = 7;
    cheh = meh + peh;
    peh = peh + 4;
}

cheh.Update();
std::cout << cheh.Get();

At the point when update is called, the Reactive peh that took place in his affecter has gone out of scope and no longer exists. However, this program and cheh's affecter execute successfully and it prints out 14 just as before. I know that the values in the lambda were passed in by the reference capture, so how can the affecter function still access the reference to peh? Does an object or integral type passed into a lambda by reference force them to persist as long as the lambda exists? Something smell's fishy...


Solution

  • I know that the values in the lambda were passed in by the reference capture, so how can the affecter function still access the reference to peh? Does an object or integral type passed into a lambda by reference force them to persist as long as the lambda exists? Something smell's fishy...

    The program has Undefined Behavior. Capturing by reference, as you suspect, does not prolong the lifetime of the objects those references are bound to, and dereferencing a reference to an object which no longer exists is UB.

    However, Undefined Behavior does not necessarily mean that a crash will occur. It is possible (as seems to be the case here) that the program will just seem to work fine. This might not be true on another machine, or after you reboot your machine.

    Also see this brilliant explanation of why it may seem that objects can be accessed even after they have fallen out of scope.