Use of const

U

Urs Thuermann

How should const be used? When I pass a pointer to some object to a
function which does not modify the object, I usually use const. But
when that pointer is later copied somewhere else, e.g. a list or
another function, I need to cast the const away, which I don't like.

class A;
class B {
const A *a;
public:
B(const A *a) : a(a) {}
void foo(std::list<A *> &l) {
l.push_back((A *)a); // the cast looks ugly
}
};

What exactly is the meaning of B(const A *a); ? Is that an assurance
to the compiler that the pointed-to object is not modified until B()
returns (enabling some optimizations), or does it mean that the object
is never modified through this pointer, not even later in some
completely other part of the program where that pointer may have been
passed to?


urs
 
V

Victor Bazarov

How should const be used?

Widely. Wisely.
> When I pass a pointer to some object to a
function which does not modify the object, I usually use const. But
when that pointer is later copied somewhere else, e.g. a list or
another function, I need to cast the const away, which I don't like.

Repeat this mantra three times a day: if your design requires a cast,
review your design!
class A;
class B {
const A *a;
public:
B(const A *a) : a(a) {}
void foo(std::list<A *> &l) {
l.push_back((A *)a); // the cast looks ugly
}
};

What exactly is the meaning of B(const A *a); ? Is that an assurance
to the compiler that the pointed-to object is not modified until B()
returns (enabling some optimizations), or does it mean that the object
is never modified through this pointer, not even later in some
completely other part of the program where that pointer may have been
passed to?

In your case since B keeps a copy of the pointer, the promise is for the
lifetime of the B object. Every 'B' pledges not to change the object
the address of which it stores and not to allow it be changed by others.
That's why no 'B' can pass its pointer to another place (store,
function) where such pledge is not made.

Why do you think you *need* the 'const' in the declaration of B::a?

V
 
S

Saeed Amrollahi

How should const be used?  When I pass a pointer to some object to a
function which does not modify the object, I usually use const.  But
when that pointer is later copied somewhere else, e.g. a list or
another function, I need to cast the const away, which I don't like.

        class A;
        class B {
                const A *a;
        public:
                B(const A *a) : a(a) {}
                void foo(std::list<A *> &l) {
                        l.push_back((A *)a);   // the cast looks ugly
                }
        };

What exactly is the meaning of B(const A *a); ?  Is that an assurance
to the compiler that the pointed-to object is not modified until B()
returns (enabling some optimizations), or does it mean that the object
is never modified through this pointer, not even later in some
completely other part of the program where that pointer may have been
passed to?

urs

Hi Urs
Besides what Victor wrote, I add the following things:
1.
const A* a; // a is a pointer to const object of A.
It means, you can't change the state of object through the pointer
a.
For various combination of const pointer, const object see the
following:
http://www.parashift.com/c++-faq-lite/const-correctness.html
2. You can't convert a pointer to a type say int to const pointer to
that type.
consider:
A my_a;
const A* p = &my_a;
A* p2 = p; // error
3. Prefer to use C++ style cast rather than C-Style cast:
l.push_back((A *)a); // the C-Style cast looks ugly
l.push_back(const_cast<A *>(a)); // C++ style cast looks more pretty

My two cents
-- Saeed Amrollahi
 
J

Joshua Maurice

3. Prefer to use C++ style cast rather than C-Style cast:
  l.push_back((A *)a);   // the C-Style cast looks ugly
  l.push_back(const_cast<A *>(a)); // C++ style cast looks more pretty

And safer. I'm not sure of the standard's guarantees offhand, but as a
matter of fact for common implementations, C-style have context
specific behavior in the presence of multiple inheritance and virtual
inheritance. Ex:
B* foo();
B* b = foo();
A* a = (A*) b;
has behavior specific to whether A and B are fully defined types or
merely forward declared at the point of the cast. If the types are
fully defined at the scope of the cast, then it behaves like a
static_cast, and it's probably what was intended. If one or both of
the types was forward declared, the cast is equivalent to a
reinterpret_cast, which is likely not what the programmer intended,
and it will produce fun to track down undefined behavior. So, if the
types are defined or declared in a header somewhere, a simple change
of that header file can /silently/ break this source.

Moral of the story? Avoid C-style casts for class types, or just in
general.
 
U

Urs Thuermann

Victor Bazarov said:
Repeat this mantra three times a day: if your design requires a cast,
review your design!
ACK.
In your case since B keeps a copy of the pointer, the promise is for
the lifetime of the B object. Every 'B' pledges not to change the
object the address of which it stores and not to allow it be changed
by others. That's why no 'B' can pass its pointer to another place
(store, function) where such pledge is not made.

OK. I've read the FAQ on constness, mentioned in another post in this
thread. And I got better understanding of const, and I didn't know
about const_cast and mutable before.
Why do you think you *need* the 'const' in the declaration of B::a?

I thought I should use const to express that class B never uses that
pointer to change the A object. Now I understand that it also
shouldn't allow others to use that pointer to modify the A object.

I see this is related to the reason subscript operators often comes in
pairs, because a subscript operators taking a T const& should also
only return a T const&. I was about to ask how one can then have a
function like

char *strstr(const char *s, const char *t);

but then I found that in C++ (unlike C) this is overloaded as

const char *strstr(const char *s, const char *t);
char *strstr( char *s, const char *t);

However, how would you implement these two functions without duplicating
code? Using the const_cast operator like this?

const char *strstr(const char *s, const char *t)
{
// increment s until t is a prefix of s
return s; // no need to cast as one would do in C
}

char *strstr(char *s, const char *t)
{
return const_cast<char*> strstr(const_cast<const char*>s, t);
}

Or is there a better way avoiding the cast altogether?

urs
 
A

Andrea Crotti

I thought I should use const to express that class B never uses that
pointer to change the A object.  Now I understand that it also
shouldn't allow others to use that pointer to modify the A object.

I see this is related to the reason subscript operators often comes in
pairs, because a subscript operators taking a T const& should also
only return a T const&.  I was about to ask how one can then have a
function like

        char *strstr(const char *s, const char *t);

but then I found that in C++ (unlike C) this is overloaded as

        const char *strstr(const char *s, const char *t);
              char *strstr(      char *s, const char *t);

However, how would you implement these two functions without duplicating
code?  Using the const_cast operator like this?

        const char *strstr(const char *s, const char *t)
        {
                // increment s until t is a prefix of s
                return s;    // no need to cast as one would do in C
        }

        char *strstr(char *s, const char *t)
        {
                return const_cast<char*> strstr(const_cast<const char*>s, t);
        }

Or is there a better way avoiding the cast altogether?

urs

I did something similar (not a guarantee :D)
Stream::Stream(const char *buffer, const int size) :
stream_is_const(true)
{
// remove the const, we keep the info in stream_is_const anyway
setStream(const_cast<char *>(buffer), size);
}

Stream::Stream(char *buffer, int size) : stream_is_const(false)
{
setStream(buffer, size);
}

but because I want to keep the information about the constness.
If you discard it I don't see why you should overload it, you should
have only one...
Whether you will modify the data you get or not, right?
 

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,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top