#include
struct foo;
int main()
{
const foo *bar;
static_assert(std::is_const::value,
\"expected co
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 &
.