Why does const_cast need to state what you're casting to?
According to the standard (§5.2.11) a const_cast casts away cv-qualifiers (const or volatile).
Here's a simple example. First you declare two functions taking a pointer and a reference:
class Bar { ... };
void foo_ptr(Bar*);
void foo_ref(Bar&);
then you create a reference-to-const:
Bar b;
const Bar& cb = b;
and then you can call either function by using the appropriate const_cast:
foo_ptr(const_c开发者_C百科ast<Bar*>(&cb));
foo_ref(const_cast<Bar&>(cb));
Here's my question: since const_cast cannot do what the other casts were designed for, isn't it obvious what you're casting to? In other words, why doesn't the language allow me to simply say:
foo_ptr(const_cast<>(&cb));
foo_ref(const_cast<>(cb));
I can think of only the following two reasons:
a) The compiler should stop me when I try to do something crazy, like:
foo_ptr(const_cast<int*>(&cb));
foo_ref(const_cast<int&>(cb));
and by forcing me to explicitly state the type I'm casting to it can then keep me from misbehaving. I find this (hypothetical) explanation weak, since it would be bizarre if the language favored allowing me to write down something wrong only to have the compiler correct me.
b) There is a possible ambiguity if the variable is both const and volatile. In that case the compiler would have no way of telling if I'm trying to cast away the one or the other (or both).
Is that why, or is there another reason?
const_cast
can be used to add or remove const
and volatile
qualifiers. So, if such a syntax were allowed, all of the following would be legitimate target types of const_cast<>(&cb)
:
Bar* (1)
const Bar* (2)
volatile Bar* (3)
const volatile Bar* (4)
You intend (1). (2) is usually silly, but it is conceivable that it might occur somewhere, perhaps in some template code. (3) and (4) are indeed the problems: you can remove the const
qualification and add the volatile
qualification all with a single cast.
You could replace the existing const_cast
with a pair of casts, const_cast
and volatile_cast
, and prohibit case (2); then you could use either of them without the target type. However, then it's more difficult to know the type of the cast expression. To know whether a cast expression adds or removes qualification, you have to know what the type of the source expression is.
There's no reason you couldn't use a function template to get what you want:
template <typename T>
T* remove_const(const T* p) {
return const_cast<T*>(p);
}
You could easily write similar functions for remove_volatile
and for add_const
and add_volatile
, which are both implicit.
I think James McNellis already touched on the important reasons. const_cast
can both add and remove constness/volatileness. It's not always clear what you want.
If I call const_cast
on an argument of type const foo
, does that mean I want to make it non-const
, or add volatile
to it? Or should it perhaps just keep the const
qualifier? If I call it on an object of type (non-const) foo
, should it add const? Or should it assume that I wanted to remove const-ness, and return an object of type (non-const) foo
as well?
Another reason may simply be consistency.
We have static_cast<T>(x)
, dynamic_cast<T>(x)
, reinterpret_cast<T>(x)
so it'd look odd to have a const_cast(x)
without the template-like syntax.
The main reason is that using const_cast
you can cast from
Foo * const * volatile * *
to
Foo * * * volatile * const
Is it really obvious what the destination type is?
精彩评论