<< PreviousNext >>

Broken container

The program here implements an interface to store any type in a class called Container. The container also provides a output functionality for the type. The main function uses this to store three different values, both by pointer and by value and then outputs them. However, it does not work correctly. Most of the pointer Containers are wrong!

The most interesting part of this problem is that on Visual Studio 2008, the program works correctly unless you turn on optimizations. I had this kind of problem in my Function interface, and it was really hard to track down since it only happened in release builds (debug builds turn off optimizations by default).

The Container class is designed so that it stores a reference to the original value, instead of making a copy of the value. This means that you need to keep any values you add to the Container alive.

// This is ok:
int v = 10;
contain(v);

// This is not:
contain(20);

So, why does this break with optimizations turned on?

main.cpp

#include <iostream>
#include <vector>
using namespace std;

/**
 * Helper for output pointers and regular values.
 */

template <class T>
struct Output {
  void operator() (const T &value) {
    cout << "Value     : " << value << endl;
  }
};

template <class T>
struct Output<T *> {
  void operator() (const T *value) {
    cout << "Pointer to: " << *value << endl;
  }
};


/**
 * Container interface, we can store any value in a container! They are
 * stored by reference, so make sure to retain the original copy of the
 * value on the stack!
 */

class Container {
public:
  virtual void output() const = 0;
};

template <class T>
class ContainerI : public Container {
public:
  ContainerI(const T &obj) : data(obj) {}

  const T &data;

  virtual void output() const {
    Output<T> t;
    t(data);
  }
};

template <class T>
Container *contain(const T &v) {
  return new ContainerI<T>(v);
}


int main() {
  int v1 = 128;
  int v2 = 256;
  int v3 = 512;

  vector<Container*> c;
  c.push_back(contain(v1));
  c.push_back(contain(&v1));
  c.push_back(contain(v2));
  c.push_back(contain(&v2));
  c.push_back(contain(v3));
  c.push_back(contain(&v3));

  for (size_t i = 0; i < c.size(); i++)
    c[i]->output();

  for (size_t i = 0; i < c.size(); i++)
    delete c[i];
  
  return 0;
}

Download files

Answer and comments