The same is done in std::vector<bool> and in std::bitset in the standard library. As stated in the reference, std::vector<bool> it returns a proxy class that has its operators overloaded to act as an element of the vector.
You could to that as well.
For a user-friendly example see again the reference for a public interface, it is something like this:
template <class Allocator>
class vector<bool, Allocator> {
  // ...
  public:
    class reference {
        friend class vector;
        reference();
      public:
        ~reference();
        operator bool() const;
        reference& operator=(bool x);
        reference& operator=(const reference&);
        void flip();
    };
  // ...
};
To implement this class you should store a member pointer to your actual data block and a mask to operate with.
For a real example, in the g++ headers look for member class of std::vector<bool> called std::vector<bool>::_Bit_reference in the file bits/stl_bvector.h.
To clarify the OP with an example:
Let's say you have a class containing 320 bools. You could write it as:
class boolcontainer {
  uint32_t data[10];
public:
  //default ctor. to initialize the elements with zeros
  boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
}
You want to add an operator[]. To add a const one is easy:
class boolcontainer {
  uint32_t data[10];
public:
  bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
}
to have a non-const one you need much more. First you need to create a class that represents a reference to your value. You must have some kind of pointer to where the value is stored and (in this case) you need a bitmask to specify one concrete bit. To be able to handle this as a bool& you need to add some operators, namely conversion to bool and operator=: 
class reference {
  uint32_t *dataptr;
  uint32_t mask;
public:
  //constructor just initializing members
  reference(uint32_t *dataptr_, uint32_t mask_) : dataptr(dataptr_), mask(mask_) {}
  //conversion to bool
  operator bool() const {
    //just like in the getter, but the bitmask is stored now locally
    return *dataptr & mask;
  }
  //sets one single bit represented by mask to b
  reference& operator=(bool b) {
    if (b) {
      *dataptr |= mask;
    } else {
      *dataptr &= ~mask;
    }
    return *this;
  }
  //TODO copy ctor., operator==, operator<
};
Note that the above struct will behave as a bool& -- reading from it reads the value from the data point represented by the pointer and the mask, and similarly, writing to it overwrites the bit at the represented location. I also wrote a constructor that initializes the members.
Now all you need is that your boolcontainer's operator[] should return an object of the above class:
class boolcontainer {
  uint32_t data[10];
public:
  boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
  class reference {
     ... //see above
  }
  //keep the const version for efficiency
  bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
  //non-const version returns our reference object.
  reference operator[](int i) { return reference(&data[i/32], 1 << (i%32)); }
};
And now some code to test it (prints only the first 40 values):
#include <iostream>
#include "boolcontainer.h"
void printboolcontainer(const boolcontainer &bc)
{
    //note that this is the constant version
    for (int i = 0; i < 40; ++i) {
        std::cout << bc[i];
    }
    std::cout << std::endl;
}
int main()
{
    boolcontainer bc;
    printboolcontainer(bc);
    bc[0] = true;
    bc[3] = true;
    bc[39] = true;
    printboolcontainer(bc);
}