Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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_cast<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?

like image 419
Alexandros Gezerlis Avatar asked Dec 08 '10 02:12

Alexandros Gezerlis


1 Answers

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.

like image 139
James McNellis Avatar answered Sep 23 '22 16:09

James McNellis