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?
*/
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?
*/