const char operator [](std::size_t n) const {return elements[n];}
This returns a const copy of elements[n]
, which is no use at all. You return a const when you don't want the caller changing your stuff, but since you're returning a copy here, you wouldn't be changing anything anyways.
Your first example is returning a const reference, which is what you should do here.