开发者

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_castcan 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 constqualifier? 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?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜