legitimate use of const_cast

139 просмотров
Перейти к первому непрочитанному сообщению

CH Lin

не прочитано,
2 окт. 1999 г., 03:00:0002.10.1999
Hi, there
I was wondering if the following is a legitimate use of const_cast.

template <typename T>
class Foo {
set<T*, some_compare_functor> S; // T* is ordered by some properties of
T

public:
void f(const T& xxx)
{
S.find(&xxx); // error! cannot covert from const T* to T* const
S.find(const_cast<T*>(&xxx)); // ok
}
};

I am reluctant to use const_cast, Is there other better way to do it? I
also tried the following:

void f(const T& xxx) {
T anotherT(xxx);
S.find(&anotherT);
}

this works but it seems defeats the purpose of passing by reference.

Any ideas? Thanks in advance.
--
CH Lin
homepage : http://pages.prodigy.net/ch.lin/

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


Carlo Wood

не прочитано,
2 окт. 1999 г., 03:00:0002.10.1999
CH Lin wrote:
>
> void f(const T& xxx) {
> T anotherT(xxx);
> S.find(&anotherT);
> }
>
> this works but it seems defeats the purpose of passing by reference.
>
> Any ideas? Thanks in advance.

Hi. Your program is very dangerous... First of all, the const_cast
can be proven not to be ok in certain circumstances that you did
not show.

Consider:

#include <set>

set<int *> s;

set<int *>::const_iterator f(const int &i)
{
return s.find(const_cast<int *>(&i));
}

int main(void)
{
int i = 4;
s.insert(&i);
*(*f(i)) = 3;
return i;
}

This will happily return `3', and thus i was changed.

But, the very fact that using `anotherT' works shows that
there is a design fault (after all, then we don't use const_cast).

The problem is of course that you use a compare function
that doesn't look at the pointer itself but at the
*contents* of the object that T* is pointing at.
That gives you the feeling that `find' won't return
an iterator to a pointer to the same object that you
passed to `f'.

I think that you at least you should check for that:

void Foo::f(const T& xxx) const // Note: you can/should ass a `const' here I think
{
T *p = *S.find(const_cast<T*>(&xxx)); // We assume that we won't find the same object.
assert( p != &xxx ); // Violation of the above assumption!
// change *p here...
}

In other words, the const cast is ok when you are 100% sure that `xxx' won't
be changed.

Carlo Wood

Siemel B. Naran

не прочитано,
3 окт. 1999 г., 03:00:0003.10.1999
On 2 Oct 1999 05:56:23 -0400, CH Lin <e....@iname.com> wrote:

> template <typename T>
> class Foo {
> set<T*, some_compare_functor> S;
>

> public:
> void f(const T& xxx)
> {
> S.find(&xxx); // error! cannot covert from const T* to T* const
> S.find(const_cast<T*>(&xxx)); // ok
> }
> };

How about

void f(T *const xxx)
{
S.find(xxx);
}


One normally thinks of pass by reference as an optimized pass by value,
or pass by value that preserves polymorphism. But if you actually use
the address of the passed variable, then make this clearly visible in
the interface. The easiest way to do this is to pass by pointer.
Incidentally, this guards us against the problem where we save the
address of a temporary.


> I am reluctant to use const_cast, Is there other better way to do it? I
>also tried the following:

If I'm not mistaken, the rule about const_cast is that only if you use
the dereferenced value of the const_cast is your program undefined.
Hence,
const char c='c';
char * oops=const_cast<char *>(&c); // ok
cout << char(*oops) << '\n'; // not sure
*oops='d'; // undefined

A stricter rule is that the const_cast itself may be undefined. Hence,
const char c='c';
char * oops=const_cast<char *>(&c); // undefined
A compiler that checks for violations of const_cast may like this rule as
it is easier to deal with. For example, given

// f.cc
#include "f.hh"
#include "g.hh"
void f(T const *const tt) { T * t=const_cast<T *>(tt); g(t); }

After compiling f.cc into f.oo, the compiler notes that function 'f'
does a const_cast on its argument. Then it calls a function 'g', but
we don't know if function 'g' dereferences the resulting pointer.
And 'g' may be part of a dynamic library, so we can't know this until
run time.

Now consider

// main.cc
#include "f.hh"
int main() { const T t; f(t); }

At link time, when the compiler looks at f.oo and main.oo, the compiler
may issue an error that 'f' does a const_cast on its argument, but the
argument is const.


> void f(const T& xxx) {
> T anotherT(xxx);
> S.find(&anotherT);
> }
>
> this works but it seems defeats the purpose of passing by reference.

I don't think the above works -- it compiles, but likely does not do
what you want. The 'anotherT' is a new object, and it's address will
not be in the set. (Well, it all depends on what your less functor is.)

--
--------------
siemel b naran
--------------

CH Lin

не прочитано,
3 окт. 1999 г., 03:00:0003.10.1999
Thanks for the reply. I think I didn't illustrate my question very well in
my original post. Let me try again.

template <typename T>
class Foo {
set<T*, some_compare_functor> S;

// T* is ordered by some properties of T

public:
void f(const T& xxx);
};

The const_cast is only used in the following context.

void Foo::f(const T& xxx) {
// if (S.find(&xxx) == S.end()) return;


// error! cannot covert from const T* to T* const

if (S.find(const_cast<T*>(&xxx)) == S.end()) return; // ok

...
}

I understand that const_cast is usually a bad thing. That's why I'm here to
ask: is this an appropriate use of const_cast?

Thanks again!
--
CH Lin -- big fan of STL
homepage : http://pages.prodigy.net/ch.lin/

CH Lin

не прочитано,
4 окт. 1999 г., 03:00:0004.10.1999
Siemel B. Naran <sbn...@uiuc.edu> wrote in message

> > void f(const T& xxx)
> > {
> > S.find(&xxx); // error! cannot covert from const T* to T* const
> > S.find(const_cast<T*>(&xxx)); // ok
> > }
> How about
>
> void f(T *const xxx)
> {
> S.find(xxx);
> }

Thanks for the reply. Becasue f() is usually called with a temporary object
argument like this:

f(T());

If using your suggestion, then the call will be:

f(&T());

I have to admit that I've never used or seen this idiom before. I don't
know if this expression is legal, but the syntax looks at least unpleasent
to me.

>
> > void f(const T& xxx) {
> > T anotherT(xxx);
> > S.find(&anotherT);
> > }
> >
> > this works but it seems defeats the purpose of passing by reference.
>
> I don't think the above works -- it compiles, but likely does not do
> what you want. The 'anotherT' is a new object, and it's address will
> not be in the set. (Well, it all depends on what your less functor is.)
>

I did provide a functor. The set just holds the pointer and the set order
is according to some property of the pointed object. That's why the above
form works. It first copy the argument to a local object. As long as T's
copy constructor does what it is supposed to do -- copying, not altering the
internal state of the copied object. Then the above form should work as I
expected.

--
CH Lin --

TiTi

не прочитано,
4 окт. 1999 г., 03:00:0004.10.1999
Hi there,

I modified some code (function f), didn't test it. But I think this might be
what you want:

void f(const T& yourT)
{
const T* const pT = (const T* const) &yourT;

S.find(pT);

// ...
}

Lemme know if this is OK (post, don't mail). If not, I'll try to do better.
Maybe you wanna post the exact signature of the template<X,X> Set::find()
method.

TiTi.

> I was wondering if the following is a legitimate use of const_cast.
>

> template <typename T>
> class Foo {
> set<T*, some_compare_functor> S; // T* is ordered by some properties
of
> T
>
> public:

> void f(const T& xxx)
> {
> S.find(&xxx); // error! cannot covert from const T* to T* const
> S.find(const_cast<T*>(&xxx)); // ok
> }

> };

Seth Jones

не прочитано,
5 окт. 1999 г., 03:00:0005.10.1999
In article <7t1fab$31be$1@newssvr04-
int.news.prodigy.com>, e....@iname.com says...
> Hi, there

> I was wondering if the following is a legitimate use of const_cast.
>
> template <typename T>
> class Foo {
> set<T*, some_compare_functor> S; // T* is ordered by some properties of
> T
>
> public:
> void f(const T& xxx)
> {
> S.find(&xxx); // error! cannot covert from const T* to T* const
> S.find(const_cast<T*>(&xxx)); // ok
> }
> };
>
> I am reluctant to use const_cast, Is there other better way to do it? I
> also tried the following:
>
> void f(const T& xxx) {
> T anotherT(xxx);
> S.find(&anotherT);
> }
>
> this works but it seems defeats the purpose of passing by reference.
>
> Any ideas? Thanks in advance.

It would be much safer to use a set of const T*
The internal structure of a set assumes that elements
will stay ordered as long as they are in the set. The
way you have defined your set, you can break the
ordering by modifying one of the elements through a
pointer.

Seth Jones

CH Lin

не прочитано,
5 окт. 1999 г., 03:00:0005.10.1999
TiTi <TJunk...@mad.scientist.com> wrote in message
news:7ta3ss$slk$1...@trex.antw.online.be...

> void f(const T& yourT)
> {
> const T* const pT = (const T* const) &yourT;
>
> S.find(pT);
>
> // ...
> }
>
> Lemme know if this is OK (post, don't mail). If not, I'll try to do
better.
> Maybe you wanna post the exact signature of the template<X,X> Set::find()
> method.
>
> TiTi.

Thanks for the reply. Your approach doesn't work. The reason is the set
has value_type T* and hence the find() expect a const reference to T*
argument. The const T* const type still can't convert to the type find()
expected.

Maybe a concrete example can make things clearer. Consider

struct cmp {
bool operator()(const int* p, const int* q) const
{ return *p < *q; }
};

set<int*, cmp> S;

void f(const int& xxx)
{
S.find(&xxx); // error
const int* const ptr = &xxx;
S.find(ptr); // still cannot covert from const int* int to int* const&
S.find(const_cast<int*>(&xxx)); // ok, cast away const
}

--
CH Lin --
homepage : http://pages.prodigy.net/ch.lin/

Siemel B. Naran

не прочитано,
5 окт. 1999 г., 03:00:0005.10.1999
On 4 Oct 1999 07:36:36 -0400, CH Lin <e....@iname.com> wrote:
>Siemel B. Naran <sbn...@uiuc.edu> wrote in message

>> > void f(const T& xxx)


>> > {
>> > S.find(&xxx); // error! cannot covert from const T* to T* const
>> > S.find(const_cast<T*>(&xxx)); // ok
>> > }

Should the member function f be const?


>If using your suggestion, then the call will be:
>
> f(&T());

You can't take the address of a temporary. The above is an error.

I thought that your set S of type std::set<T*> is for storing pointers.
But it seems that it is for storing objects, because the less functor
compares the pointed to objects. So I'll assume that your set is for
storing polymorphic objects.

I think the use of const_cast inside the member function f is ok, but
it seems shaky to me. What happens if a non-const version of the
original object is not available, as in,
const T t(1,2,3);
object.find(t);
A non-constant version of 't' is not available, so the const_cast
seems like it should be undefined. However, only if you write to
the variable will you get undefined behavior, and if you just read
't' then I don't think you should get undefined behavior. Still,
this makes me a little uneasy. I don't like to use const_cast on
external pointers.

To make things safer, store pointers to const inside the map.
std::set<T const *, Less> d_Set; // private data member of class Object
Now member function f will not cast away constness of the external
pointer it receives in its function argument list. However, member
functions of class Object will cast away constness of the pointers
in the map as needed. But we know for certain that non-const
versions of these objects are available, so the const_cast is
safe though a little cumbersome.

If member function f is a const member function, then you could use
std::set<logical_ptr<T>,Less> d_Set;
Note: the non-const set::find returns a "logical_ptr<T>&".
And logical_ptr<T>::operator->() returns a "T *".
Note: the const set::find returns a "const logical_ptr<T>&".
And logical_ptr<T>::operator->() const returns a "T const *".

--
--------------
siemel b naran
--------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jens Kilian

не прочитано,
6 окт. 1999 г., 03:00:0006.10.1999
sbn...@uiuc.edu (Siemel B. Naran) writes:
> One normally thinks of pass by reference as an optimized pass by
> value, or pass by value that preserves polymorphism. But if you
> actually use the address of the passed variable, then make this
> clearly visible in the interface. The easiest way to do this is to
> pass by pointer. Incidentally, this guards us against the problem
> where we save the address of a temporary.

OTOH, pass-by-pointer allows a caller to pass a null pointer even if that
is not a sensible value. I always use references when I don't want to get
(and check for) a null pointer.

Bye,
Jens.
--
mailto:j...@acm.org phone:+49-7031-14-7698 (HP TELNET 778-7698)
http://www.bawue.de/~jjk/ fax:+49-7031-14-7351
PGP: 06 04 1C 35 7B DC 1F 26 As the air to a bird, or the sea to a fish,
0x555DA8B5 BB A2 F0 66 77 75 E1 08 so is contempt to the contemptible. [Blake]

CH Lin

не прочитано,
6 окт. 1999 г., 03:00:0006.10.1999
Seth Jones <se...@kansmen.com> wrote in message

> It would be much safer to use a set of const T*
> The internal structure of a set assumes that elements
> will stay ordered as long as they are in the set. The
> way you have defined your set, you can break the
> ordering by modifying one of the elements through a
> pointer.
>
> Seth Jones

Thanks for the reply. I did think about using const T*. The problem is I
need to use that pointer to modify the pointed object (not the key field, of
course). How do I deal with such situation?

--
CH Lin --
homepage : http://pages.prodigy.net/ch.lin/

Stefan Seefeld

не прочитано,
6 окт. 1999 г., 03:00:0006.10.1999
CH Lin wrote:
>
> Seth Jones <se...@kansmen.com> wrote in message
> > It would be much safer to use a set of const T*
> > The internal structure of a set assumes that elements
> > will stay ordered as long as they are in the set. The
> > way you have defined your set, you can break the
> > ordering by modifying one of the elements through a
> > pointer.
> >
> > Seth Jones
>
> Thanks for the reply. I did think about using const T*. The problem is I
> need to use that pointer to modify the pointed object (not the key field, of
> course). How do I deal with such situation?

Yeah, the point is really about what const means to different parties.
There might not always be a solution. Is it possible that you make all
the members which are not affected by set ordering mutable ?
This way you could safely create a const T * and modifying it simply
because you defined constness to mean just that.

struct T
{
bool operator > (const T &t) { return key > t.key;}
int key;
mutable int data;
};


Stefan
_______________________________________________________

Stefan Seefeld
Departement de Physique
Universite de Montreal
email: seef...@magellan.umontreal.ca

_______________________________________________________

...ich hab' noch einen Koffer in Berlin...

Siemel B. Naran

не прочитано,
6 окт. 1999 г., 03:00:0006.10.1999
On 6 Oct 1999 12:40:58 -0400, CH Lin <e....@iname.com> wrote:
>Seth Jones <se...@kansmen.com> wrote in message

>> It would be much safer to use a set of const T*

>Thanks for the reply. I did think about using const T*. The problem is I


>need to use that pointer to modify the pointed object (not the key field, of
>course). How do I deal with such situation?

The functions that need to a "T *" would do a const_cast to transform the
existing "T const *" in the map to "T *". BTW, I'm confused about what
a set is. What is set<T>::value_type? And what is set<T>::iterator and
set<T>::const_iterator? And what does each iterator's operator* return?

--
--------------
siemel b naran
--------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

TiTi

не прочитано,
8 окт. 1999 г., 03:00:0008.10.1999
I'm sorry, I thought the find method expected a const pointer. I didn't get
the exact signature of set<X,Y>::find. I don't use the standard template
library.

TiTi

Scott Meyers

не прочитано,
12 окт. 1999 г., 03:00:0012.10.1999
On 5 Oct 1999 16:21:40 -0400, Siemel B. Naran wrote:
> > f(&T());
>
> You can't take the address of a temporary. The above is an error.

I suspect this is true only for built-in types. If T is a user-defined
type, the above isn't taking the address of a temporary, it's an invocatin
of the member function operator& on a temporary, and that's allowed.

I point this out only because of the generalization that many (most?)
restrictions on rvalues fail to apply to user-defined types, because what
look like lvalue-only operations are really member function calls.

Scott

--
Scott Meyers, Ph.D. sme...@aristeia.com
Software Development Consultant http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD

Ответить всем
Написать сообщение автору
Переслать
0 новых сообщений