#include <type_traits> struct foo; int main() { const foo *bar; static_assert(std::is_const<decltype(*bar)>::value, "expected const but this is non-const!"); }
This results in a failing static_assert
which is unexpected. This is somewhat similar to this question on const references but not quite the same.
In my case, dereferencing bar
should give an instance of const foo
as its type but yet std::is_const
is saying otherwise.
Shortly that's because a reference or a pointer to a const type is not a const type.
Note that decltype(*bar)
isn't const foo
, it's const foo &
and they are really different beasts.
Consider the example given here:
std::cout << std::is_const<const int *>::value << '\n'; // false std::cout << std::is_const<int * const>::value << '\n'; // true
We see that std::is_const<const int *>::value
is false and std::is_const<int * const>::value
is true.
That's because in const int *
the type is pointer to something const, that is not a const type as intended by is_const
(and the standard actually). In int * const
the const qualifier applies to the pointer type and not to the pointed one, thus the type is a const one, no matter to what it points.
Something similar applies for const foo &
, that is a reference to something const.
You can solve using this instead:
static_assert(std::is_const<std::remove_reference_t<decltype(*bar)>>::value, "expected const but this is non-const!");
Or even this, for you don't need to do *bar
actually:
static_assert(std::is_const<std::remove_pointer_t<decltype(bar)>>::value, "expected const but this is non-const!");
In this case, by removing the pointer/reference with remove_pointer_t
/remove_reference_t
your type becomes const foo
, that is actually a const type.
As a side note, the example above uses the C++14-ish std::remove_reference_t
and std::remove_pointer_t
type traits.
You can easily turn those lines of code to C++11 as it follows:
static_assert(std::is_const<typename std::remove_pointer<decltype(bar)>:: type>::value, "expected const but this is non-const!");
It's worth mentioning a few comments to the answer to give more details:
Thanks to @DanielFischer for the question:
Is there a short explanation why
decltype(*bar)
isconst foo&
rather thanconst foo
?
I'm not a language-lawyer, but I guess it can be deduced from [expr.unary.op]/1 (emphasis mine):
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
And [dcl.type.simple]/4.4 (emphasis mine):
otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
Both referring to the working draft.
Thanks to @LightnessRacesInOrbit for the comment. Note that decltype(*bar)
being const foo &
is a funny C++ quirk of decltype
, since *bar
is not const foo &
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With