Emulating special constructors for const objects with a wapper class in C++
I spent a few hours today figuring out how to make the cleanest possible const-correct code for a class that contains a reference or pointer member, whose constness is protected by accessor functions. Here is an example class:
- class Example
- {
- public:
- Example (Data & data) : data (data) {}
- Data & data() { return data; }
- Data const & data() const { return data; }
- /* More stuff here in a real situation ... */
- private:
- Data & data;
- /* More stuff here in a real situation ... */
- };
The problem with this class arises when you want to instantiate a const Example from a const Data object. The constructor wanẗ́s a non-const reference in any case, and C++ does not support special constructors for const objects. The cleanest solution I came up with, was making a ConstExample class (similar to const_iterator in STL). I ended up implementing a simple wrapper with a bunch of overridden operators:
- class ConstExample
- {
- public:
- ConstExample (Data const & data)
- : example (const_cast<Data &>(data)) {}
- inline operator Example const & () { return example; }
- inline Example const * operator& () { return &example; }
- inline Example const * operator-> () { return &example; }
- inline Example const & operator() () { return example; }
- private:
- Example example;
- };
- A const_cast is involved, but that is rather inevitable because of the limitations of C++
- The first two operators make it possible to pass the object as a const reference or pointer to an Example object.
- The third and fourth make it possible to call const functions (or reference members) of Example. You'll probably want to use only one of these, if any. In my case, the object will mostly just be passed to functions as a reference.
This might not be the best way to do things in all cases. Sometimes splitting the main class into a const base class, which is inherited by a non-const class might be a better solution (e.g. Symbians descriptor classes). It all depends on the use case and semantics of const.
The environment I'm using this pattern in, has separate overrides for functions that handle non-const and const data. The const data often needs to be copied for processing, while the non-const version can save memory by doing in-place processing. Also, most of the time const objects are constructed from non-const data, only to tell others that the data should not be changed. So, in my case this pattern is very transparent.
I also considered a static factory function (i.e. Example const e = Example::Const (data);) and some kind of solution using template specializations, but ended up using a wrapper class instead.
If you know of some other solution (or a very clean template-based solution) please leave a comment!
Comments
Post new comment