C++
I want to know if a pointer that isn’t already a two-const
pointer (e.g. T const * const
) can be implicitly or explicitly cast, processed through something (e.g. a function), or otherwise converted, to yield a T const * const
, without or before being used to initialize a variable that is declared as a T const * const
. How can I do this?
I thought that if I started with a T*
, then one const_cast
(or two, in case the cast can only cast in one const
at a time) would suffice, but apparently it doesn’t. Many variables in the code show a different unsuccessful attempt at yielding T const * const
through casting or returning from a function. Every cast failed to return a pointer with a trailing const
. (I refer to the const
to the left of *
as the leading const
and the one to the right of *
as the trailing const
.) Because of the unsuccessful casts, I tried unsuccessfully to force the const
through direct initialization. This was compiled in VC11
. g++ on stack-crooked.com gives a logically equivalent console output, albeit with different names for typeid(/*...*/).name()
.
#include <iostream>
#include <typeinfo>
using namespace std;
int const * const foo()
{
return nullptr;
}
int main()
{
int x = 7;
auto a1 = &x;
cout << typeid(a1).name() << endl;
auto a2 = const_cast<int const *>(&x);
cout << typeid(a2).name() << endl;
auto a3 = const_cast<int * const>(&x);
cout << typeid(a3).name() << endl;
auto a4 = const_cast<int const * const>(&x);
cout << typeid(a4).name() << endl;
auto a5 = const_cast<int const * const>(a4);
cout << typeid(a5).name() << endl;
auto a6 = (int const * const) &x;
cout << typeid(a6).name() << endl;
auto a7 = static_cast<int const * const>(a4);
cout << typeid(a7).name() << endl;
auto a8 = reinterpret_cast<int const * const>(a4);
cout << typeid(a8).name() << endl;
auto a9 = foo();
cout << typeid(a9).name() << endl;
int const * const a10 = &x;
cout << typeid(a10).name() << endl;
cout << ( typeid(a10) == typeid(a4) ) << endl;
auto a12 = a10;
cout << typeid(a12).name() << endl;
cout << ( typeid(a12) == typeid(a4) ) << endl;
}
Expected results vs. actual results, and questions:
Question numbers correspond to the same-numbered a#
variables.
int *
int const *
int* const
, but got int*
. Did the const_cast
ignore its trailing const
argument and why? Since the return is the same constness and type as the argument, did the cast run at all?int const * const
, but got int const*
. Did the const_cast
ignore its trailing const
argument and why?const_cast<int const * const>
will include a trailing const
in the result given that the argument a4
already has a leading const
. Expected int const * const
. Got int const*
. Did the const_cast
ignore its trailing const
argument and why?int const * const
. Got int const*
. Why would the explicit cast still exclude the trailing const
?int const * const
. Got int const*
. Why would the static_cast
exclude the trailing const
?int const * const
. Got int const*
. Why would the reinterpret_cast
exclude the trailing const
?int const * const
. Got int const*
. Why would an initialization to the int const * const
return of a function still exclude the trailing const
in the result?int const * const
. Got int const*
from the console output but not from the debugger. a10
is explicitly declared as int const * const
, so why does the typeid().name()
exclude the trailing const? operator==
yields 1
, so why is the typeid()
itself (not just the name) for a10
equivalent to that of a4
? The VC11 debugger lists a10
’s type as int const * const
. Why is it different than the one from typeid()
and typeid().name()
? Which one is correct?a11
omitted because it looks like the word “all”.a12
to be int const * const
because it is initialized to a10
, which was explicitly declared int const * const
. operator==
yields 1
, so typeid()
is still int const*
. Got int const*
from both the console output and the debugger. Why are they different from the expected result?Are all casts, function returns, and initializations limited to only casting in one const
at a time? Is the leading const
the only thing they can cast in?
const_cast
does work the way you think it does. However, auto
doesn't do what you think it does. auto
works similarly to function template parameter deduction (in fact, it's defined in terms of the latter). Now consider:
template<typename T>
void f(T x);
f(42);
int const n = 42;
f(n);
Both calls are to f<int>()
, not f<const int>()
. Top-level const modifiers are ignored by template parameter deduction. For the same reason, in this example
auto a = 42; a = 84;
auto b = n; b = 84;
variables a
and b
are of type int
, not const int
, and can be modified.