Why is std::is_const::value 'false' even though T's value_type is const?

后端 未结 1 1903
南方客
南方客 2021-02-12 05:10
#include 

struct foo;
int main()
{
    const foo *bar;

    static_assert(std::is_const::value,
                  \"expected co         


        
相关标签:
1条回答
  • 2021-02-12 05:59

    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) is const foo& rather than const 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 &.

    0 讨论(0)
提交回复
热议问题