would you consider this function const?

J

john

Are the following functions an abuse of good usage of 'const'? I think
func3 is an abuse, but I'm not sure if func1 or func2, in isolation,
would be considered poor usage since they do not actually modify an
instance of A themselves (although they do leave the door open). Thanks.

class B;

class A
{

B* m_b;

public:

void func1(A* &a) const
{
a = const_cast<A*>(this);
}

B* func2() const { return m_b; }

// because we could do
void func3() const
{
// now we can change m_b;
B* b = func2();

// now we can change this;
A* a;
func1(a);
}

};
 
V

Victor Bazarov

Are the following functions an abuse of good usage of 'const'? I think
func3 is an abuse, but I'm not sure if func1 or func2, in isolation,
would be considered poor usage since they do not actually modify an
instance of A themselves (although they do leave the door open). Thanks.

class B;

class A
{

B* m_b;

public:

void func1(A* &a) const
{
a = const_cast<A*>(this);
}

B* func2() const { return m_b; }

// because we could do
void func3() const
{
// now we can change m_b;
B* b = func2();

// now we can change this;
A* a;
func1(a);
}

};

IME 'const_cast' is good only for one thing: to add const where one
needs an overload resolution to go a particular way. Any other use of
'const_cast' is an instance of abuse. That's just IME, of course.

As with 'func2', there is nothing inherently wrong. The pointer is
const, but the object to which the pointer points isn't. If the object
to which m_b points is *owned* by 'this', then I think a slight change
in design might accomplish the intended protection:

const B* func2() const { return m_b; }
B* funct2() { return m_b; }

Otherwise, if m_b does not designate ownership, and hence does not call
for ensuring protection, all is well.

V
 
J

john

On 12/14/2011 10:17 AM, john wrote: snipped ....

IME 'const_cast' is good only for one thing: to add const where one
needs an overload resolution to go a particular way. Any other use of
'const_cast' is an instance of abuse. That's just IME, of course.

Could you give an example of the use of const_cast in this case? I do
not quite follow what you are saying.
As with 'func2', there is nothing inherently wrong. The pointer is
const, but the object to which the pointer points isn't. If the object
to which m_b points is *owned* by 'this', then I think a slight change
in design might accomplish the intended protection:

const B* func2() const { return m_b; }
B* funct2() { return m_b; }

Otherwise, if m_b does not designate ownership, and hence does not call
for ensuring protection, all is well.

This makes sense. Thanks.

John
 
V

Victor Bazarov

Could you give an example of the use of const_cast in this case? I do
not quite follow what you are saying.

This is from memory, I don't have a good working example right now...
When you want to call a const member function for a non-const object,
and a non-const member function also exists and overloads the const one,
the non-const would be picked - it's a better match. That's where you
could force the compiler into picking the one you want:

#include <iostream>
#include <ostream>
struct foo {
void bar() const { std::cout << "const bar\n"; }
void bar() { std::cout << "regular bar\n"; }
};

int main() {
// ...
foo f;
f.bar(); // non const is picked.
const_cast<const foo&>(f).bar(); // const version
}

...something like that, anyway. It also happens in the context of a
non-const member that is [partially] implemented in terms of a const
member function, for instance. This is artificial, of course:

struct foo {
void bar() const {
std::cout << "const bar\n"; }
void bar() {
std::cout << "non-";
const_cast<const foo*>(this)->bar(); }
};

HTH

V
 
J

john

Could you give an example of the use of const_cast in this case? I do
not quite follow what you are saying.

This is from memory, I don't have a good working example right now...
When you want to call a const member function for a non-const object,
and a non-const member function also exists and overloads the const one,
the non-const would be picked - it's a better match. That's where you
could force the compiler into picking the one you want:

#include <iostream>
#include <ostream>
struct foo {
void bar() const { std::cout << "const bar\n"; }
void bar() { std::cout << "regular bar\n"; }
};

int main() {
// ...
foo f;
f.bar(); // non const is picked.
const_cast<const foo&>(f).bar(); // const version
}

..something like that, anyway. It also happens in the context of a
non-const member that is [partially] implemented in terms of a const
member function, for instance. This is artificial, of course:

struct foo {
void bar() const {
std::cout << "const bar\n"; }
void bar() {
std::cout << "non-";
const_cast<const foo*>(this)->bar(); }
};

HTH

V

I see. That makes sense.

One of the issues that spurred this question (related to my func1 in the
original post) is the following. I have a 3D mesh composed of
Volumes/Faces/Edges/Vertices. So, I have a Volume, Face, Edge, and
Vertex class, each derived from a base Topology class. The mesh is
consistent, so given a particular instance of one of the classes, there
are functions that return geometry information such as connectivity. In
particular, each class has a virtual function (declared as pure virtual
at the base Topology class)

virtual void GetVolumes(std::set<Volume*> volumes);

that returns a list of pointers to the volumes associated with the
entity. I want to make this function 'const' since it does not change
the element. For a Face, Edge, and Vertex, this poses no problem.
However, for a volume instance, the function should return a pointer to
just itself, so if GetVolumes is const, then I have

void Volume::GetVolumes(std::set<Volume*> volumes) const
{
volumes.clear();
volumes.insert(const_cast<Volume*>(this));
}

I do not see how to have the GetVolumes function const without the use
of const_cast in the Volume::GetVolumes function. Is there a more
intelligent way to get the same result?

Thanks,
John
 
V

Victor Bazarov

On 12/14/2011 11:19 AM, Victor Bazarov wrote:
On 12/14/2011 10:17 AM, john wrote:
snipped ....

IME 'const_cast' is good only for one thing: to add const where one
needs an overload resolution to go a particular way. Any other use of
'const_cast' is an instance of abuse. That's just IME, of course.

Could you give an example of the use of const_cast in this case? I do
not quite follow what you are saying.

This is from memory, I don't have a good working example right now...
When you want to call a const member function for a non-const object,
and a non-const member function also exists and overloads the const one,
the non-const would be picked - it's a better match. That's where you
could force the compiler into picking the one you want:

#include <iostream>
#include <ostream>
struct foo {
void bar() const { std::cout << "const bar\n"; }
void bar() { std::cout << "regular bar\n"; }
};

int main() {
// ...
foo f;
f.bar(); // non const is picked.
const_cast<const foo&>(f).bar(); // const version
}

..something like that, anyway. It also happens in the context of a
non-const member that is [partially] implemented in terms of a const
member function, for instance. This is artificial, of course:

struct foo {
void bar() const {
std::cout << "const bar\n"; }
void bar() {
std::cout << "non-";
const_cast<const foo*>(this)->bar(); }
};

HTH

V

I see. That makes sense.

One of the issues that spurred this question (related to my func1 in the
original post) is the following. I have a 3D mesh composed of
Volumes/Faces/Edges/Vertices. So, I have a Volume, Face, Edge, and
Vertex class, each derived from a base Topology class. The mesh is
consistent, so given a particular instance of one of the classes, there
are functions that return geometry information such as connectivity. In
particular, each class has a virtual function (declared as pure virtual
at the base Topology class)

virtual void GetVolumes(std::set<Volume*> volumes);

That doesn't look right. Did you lose the ampersand before the argument
name?
that returns a list of pointers to the volumes associated with the
entity. I want to make this function 'const' since it does not change
the element. For a Face, Edge, and Vertex, this poses no problem.
However, for a volume instance, the function should return a pointer to
just itself, so if GetVolumes is const, then I have

void Volume::GetVolumes(std::set<Volume*> volumes) const
{
volumes.clear();
volumes.insert(const_cast<Volume*>(this));
}

I do not see how to have the GetVolumes function const without the use
of const_cast in the Volume::GetVolumes function. Is there a more
intelligent way to get the same result?

What happens if you change the type of the argument to

std::set<const Volume*>

? If it's a set of constant volumes, that's what the type should
express. If you find it difficult to type, use a typedef *or* introduce
some kind of 'Volume proxy' and keep that instead of the pointer to Volume.

V
 
J

john

That doesn't look right. Did you lose the ampersand before the argument
name?

Yes, it should be

What happens if you change the type of the argument to

std::set<const Volume*>

? If it's a set of constant volumes, that's what the type should
express. If you find it difficult to type, use a typedef *or* introduce
some kind of 'Volume proxy' and keep that instead of the pointer to Volume.

I will have to look at whether I can do that or not. My guess is that,
for the most part, I will need non-const Volume*'s. It's a huge code
base, so a change like that could ripple to a lot of places.

John
 
V

Victor Bazarov

Yes, it should be



I will have to look at whether I can do that or not. My guess is that,
for the most part, I will need non-const Volume*'s. It's a huge code
base, so a change like that could ripple to a lot of places.

Well, depending on the domain and the purpose for which the pointers are
collected, it might be OK to const_cast them like that. After all, it
is unlikely that any object of your Volume class is ever created in a
constant memory, so the 'const' here is just a pretense. Keep in mind,
however, that const_cast can be abused. And don't abuse it. If you can
rework your design in such a way that wouldn't call for the use of
'const_cast' to cast *away* any constness, it could lead to a more
robust system, methinks.

I wonder (not that you need to explain, just wonder along with me), how
is Volume and, say, Edge derive from the same class. What is common
about them (aside from 'GetVolumes')? Also, can you have nested
Volumes? You know, an egg inside a duck inside a hare hidden in a
trunk... Seems like if your answer is 'no', then a Volume cannot derive
from Topology since you essentially make Topology aware of a type that
will be derived from it, which is usually a no-no...

V
 
J

john

What happens if you change the type of the argument to
Well, depending on the domain and the purpose for which the pointers are
collected, it might be OK to const_cast them like that. After all, it is
unlikely that any object of your Volume class is ever created in a
constant memory, so the 'const' here is just a pretense. Keep in mind,
however, that const_cast can be abused. And don't abuse it. If you can
rework your design in such a way that wouldn't call for the use of
'const_cast' to cast *away* any constness, it could lead to a more
robust system, methinks.

I wonder (not that you need to explain, just wonder along with me), how
is Volume and, say, Edge derive from the same class. What is common
about them (aside from 'GetVolumes')? Also, can you have nested Volumes?
You know, an egg inside a duck inside a hare hidden in a trunk... Seems
like if your answer is 'no', then a Volume cannot derive from Topology
since you essentially make Topology aware of a type that will be derived
from it, which is usually a no-no...


Well, a lot of functionality is common to all element types. For
example, every Topology (be it a volume, edge, face, vertex) has
functions that can return its unique id, its center point, its bounding
vertices, its 'type', its dimension. Every Topology stores another
class object that can compute geometry on itself, for example position
vector, unitary vectors, etc. So there is a lot of common functionality
that is declared as pure virtual or virtual at the base class and some
common data members.

To answer you other question, nested volumes are not allowed. I do not
understand you comment that Volume cannot derive from Topology. I do
not see a problem with doing it.
 
V

Victor Bazarov

[...] I do not
understand you comment that Volume cannot derive from Topology. I do not
see a problem with doing it.

Nah... Forget I mentioned it. Do a const_cast. Five years from now
expect a maintenance nightmare. If it doesn't materialize, consider
yourself lucky. If your program doesn't live that long, all the better.

V
 
N

none

IME 'const_cast' is good only for one thing: to add const where one
needs an overload resolution to go a particular way. Any other use of
'const_cast' is an instance of abuse. That's just IME, of course.

I would tolerate const_cast inside a method when conceptually, a
method is const (i.e. does not conceptually change the object) but due
to implementation details, the object internal needs to be modified.

I very rarely see a reason to use it. But for example, some peoples
are fans of delaying initialisation using a pattern like:

class A
{
public:
A(): m_initDone(false), ... {};
std::string foo() const
{
if(!m_initDone)
{
const_cast<A *>(this)->init();
}
// do the work
}
private:
void init() {...};
bool m_initDone;
// other member variables
}

I am not a big fan of the above but this would be an example where
const_cast allow an object to modify its internals without breaking
the concept that the object has not changed.

Unfortunately, most of the time const_cast is used to cast-away
constness from code that is const-correct to interface with
const-incorrect code when the correct thing to do would be fix the
const-incorrect code :-( (Actually, most of the time I see it, C-style
hard casts are being used which is even worse).


Yannick
 
T

Tobias Müller

none said:
I very rarely see a reason to use it. But for example, some peoples
are fans of delaying initialisation using a pattern like:

class A
{
public:
A(): m_initDone(false), ... {};
std::string foo() const
{
if(!m_initDone)
{
const_cast<A *>(this)->init();
}
// do the work
}
private:
void init() {...};
bool m_initDone;
// other member variables
}

I am not a big fan of the above but this would be an example where
const_cast allow an object to modify its internals without breaking
the concept that the object has not changed.

IMO it's better to declare the members that are touched by the
initialization as 'mutable'. In most cases, those are only a few large
objects.
This allows you to declare the init() method as const and you don't have to
cast anything.

Tobi
 
J

Joshua Maurice

IMO it's better to declare the members that are touched by the
initialization as 'mutable'. In most cases, those are only a few large
objects.
This allows you to declare the init() method as const and you don't have to
cast anything.

Why would you ever declare an init method as const? An init method
must be the poster child of a method which modifies the object.

Upon 2 seconds of reflection, is this because you're writing code
without exceptions and more or less without constructors? I guess in
that light your comment isn't incredibly silly - a const object still
needs to be initialized, and in that case one isn't using constructors
because one isn't using exceptions, and one has decided to go with the
init method approach as opposed to the zombie object flag.
 
T

Tobias Müller

Joshua Maurice said:
Why would you ever declare an init method as const? An init method
must be the poster child of a method which modifies the object.

Upon 2 seconds of reflection, is this because you're writing code
without exceptions and more or less without constructors? I guess in
that light your comment isn't incredibly silly - a const object still
needs to be initialized, and in that case one isn't using constructors
because one isn't using exceptions, and one has decided to go with the
init method approach as opposed to the zombie object flag.

If you didn't read the post I was referring to: we're talking about /lazy/
initialization, i.e. initialization on first use. The first use could be a
const method and if you don't want to const_cast, init must be const which
is only possible if the initialized members are declared mutable.

Imagine for example an XML DOM tree that is only parsed on demand, and only
those parts that are actually used.

Tobi
 
J

Joshua Maurice

If you didn't read the post I was referring to: we're talking about /lazy/
initialization, i.e. initialization on first use. The first use could be a
const method and if you don't want to const_cast, init must be const which
is only possible if the initialized members are declared mutable.

Imagine for example an XML DOM tree that is only parsed on demand, and only
those parts that are actually used.

I see. My apologies. The init method isn't a public exposed function,
but merely a internal implementation detail.

(The alternative is of course to const_cast before calling init, and
having init be non-const. I don't realy think there's much of a
difference between the two approaches, so nevermind.)
 

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,770
Messages
2,569,584
Members
45,077
Latest member
SangMoor21

Latest Threads

Top