beatwaves.net

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:

  1. class Example
  2. {
  3. public:
  4.     Example (Data & data) : data (data) {}
  5.  
  6.     Data &       data()       { return data; }
  7.     Data const & data() const { return data; }
  8.  
  9.    /* More stuff here in a real situation ... */
  10.  
  11. private:
  12.     Data & data;
  13.    /* More stuff here in a real situation ... */
  14. };

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:

  1. class ConstExample
  2. {
  3. public:
  4.     ConstExample (Data const & data)
  5.       : example (const_cast<Data &>(data)) {}
  6.  
  7.     inline operator Example const & () { return example; }
  8.     inline Example const * operator& () { return &example; }
  9.     inline Example const * operator-> () { return &example; }
  10.     inline Example const & operator() () { return example; }
  11.  
  12. private:
  13.     Example example;
  14. };
  • 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!

Tags:

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
minus six equals minus ten
Solve this math question and enter the solution with digits. E.g. for "two plus four = ?" enter "6".