Necessity for const and non-const containers

  • Thread starter Matteo Settenvini
  • Start date
M

Matteo Settenvini

/*

Hello,

this is a recurring problem I've encountered (though not so often), and
searching the Internet does not crop up a lot of answers, so I'm asking
you what you think.

The main problem arises when you have a variable that can be both const
or non-const, and you want to save it in a custom container for later
use. You would like to be able to:

* always call the const methods on the variable from the container,

and

* to call the non-const methods that modify the containee only when
the container has been constructed with a non-const parameter.

Here I present a solution using templates. However, this leads to the
undesirable effect of two template instantiations, depending either if
you are using a const or non-const parameter, and has a not-so-pretty
syntax.

What do you think? Are there more elegant ways to achieve this result?
const_cast<> is definitely NOT elegant.

Thanks,
Matteo Settenvini

*/


class A
{
public:
A () : a(0) {}

int increment_a () { return a++; }
int inspect_a () const { return a; }

private:
int a;
};


class ContainsA
{
public:
// ContainsA (const A& a) : _a (&a) {} // Compilation error
ContainsA (A& a) : _a (&a) {}

int change () { return _a->increment_a (); }
int keep () const { return _a->inspect_a (); }

private:
A *_a;

/*
PROBLEM 1:
...this does not compile, because we are trying
to assign a "const A*" to a "A*" (thus violating
constness constraints).

PROBLEM 2:
...the alternative of defining _a as "const A*" does not
allow me to call increment_a () in a non-const
method of ContainsA, when ContainsA
was built using the non-const-parameter constructor.
*/
};

/*
Solution with a template:
*/

// Taken from http://www2.research.att.com/~bs/bs_faq2.html#constraints
template<class T, class B>
struct Derived_from
{
static void constraints (T *p) { B *pb = p; }
Derived_from () { void(*p)(T *) = constraints; }
};

template<typename T>
class TemplatedContainsA
{
public:
TemplatedContainsA (T& a) : _a (&a)
{ Derived_from<T, const A> (); }

int change () { return _a->increment_a (); }
int keep () const { return _a->inspect_a (); }

private:
T *_a;
};


void
f (const A& a)
{
// ContainsA c (a); // Compilation error
// c.keep (); // GOAL: We want this to be allowed,
// without defining both a const and non-const
// version of ContainsA (e.g. class
// ConstContainsA), even if ContainsA can
// possibly inherit from ConstContainsA (but I
// don't like this, because sounds bad from
// a IS-A perspective).

TemplatedContainsA<const A> tc (a); // not pretty, but it works
tc.keep ();
}

int
main (int, char **)
{
A a;
ContainsA c (a);
TemplatedContainsA<A> tc (a);

c.keep ();
c.change ();

tc.keep ();
tc.change ();

f (a);
}

/*
QUESTION 1:
Are there better ways? I'm searching for something
better and cleaner.

QUESTION 2:
This will instantiate two different versions of the template,
one for the const and one for the non-const parameter,
thus leading to an increased code size. Can we avoid this?
*/
 
G

Goran

/*

Hello,

this is a recurring problem I've encountered (though not so often), and
searching the Internet does not crop up a lot of answers, so I'm asking
you what you think.

The main problem arises when you have a variable that can be both const
or non-const, and you want to save it in a custom container for later
use. You would like to be able to:

  * always call the const methods on the variable from the container,

and

  * to call the non-const methods that modify the containee only when
the container has been constructed with a non-const parameter.

That would be run-time const-correctness, which is something absent
from the language. I would say that you are pushing things too far.

I don't know what is your use-case, and so I don't see why simply
having const and non-const instances of the container isn't enough for
you. Care to expand?
Here I present a solution using templates. However, this leads to the
undesirable effect of two template instantiations, depending either if
you are using a const or non-const parameter, and has a not-so-pretty
syntax.

Some build chains (or perhaps only one?) eliminate such duplicate
code. ;-)

Goran.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top