为什么std :: is_const :: value'false'即使T的value_type是const?

gre*_*olf 24 c++ const c++11

#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!");
}
Run Code Online (Sandbox Code Playgroud)

这导致static_assert意外失败.这有点类似于关于const引用的这个问题但不完全相同.

在我的情况下,解除引用bar应该给出一个const foo类型的实例,但是std::is_const却说不然.

sky*_*ack 34

不久之后,因为引用或指向const类型的指针不是const类型.
请注意,decltype(*bar)不是const foo,const foo &它们是真正不同的野兽.


考虑这里给出的例子:

std::cout << std::is_const<const int *>::value << '\n'; // false
std::cout << std::is_const<int * const>::value << '\n'; // true
Run Code Online (Sandbox Code Playgroud)

我们认为这std::is_const<const int *>::value是错误的,std::is_const<int * const>::value是真的.
那是因为在const int *类型中是指向const的指针,这不是一个const类型is_const(实际上是标准).在int * constconst限定符适用于指针类型,而不是尖锐之一,因此类型是const的一个,无论它指向.
类似的东西适用const foo &,这是对const的引用.

您可以使用此解决方案:

static_assert(std::is_const<std::remove_reference_t<decltype(*bar)>>::value, "expected const but this is non-const!");
Run Code Online (Sandbox Code Playgroud)

或者甚至是这样,因为你不需要*bar实际做到:

static_assert(std::is_const<std::remove_pointer_t<decltype(bar)>>::value, "expected const but this is non-const!");
Run Code Online (Sandbox Code Playgroud)

在这种情况下,通过去除所述指针/引用remove_pointer_t/ remove_reference_t你的类型变为const foo,即实际上是一个常数类型.


作为旁注,上面的例子使用C++ 14-ish std::remove_reference_tstd::remove_pointer_t类型特征.
您可以轻松地将这些代码行转换为C++ 11,如下所示:

static_assert(std::is_const<typename std::remove_pointer<decltype(bar)>:: type>::value, "expected const but this is non-const!");
Run Code Online (Sandbox Code Playgroud)

值得一提的是对答案的一些评论,以提供更多细节:

  • 感谢@DanielFischer提出的问题:

    是否有一个简短的说明,为什么decltype(*bar)const foo&不是const foo

    我不是语言律师,但我想它可以从[expr.unary.op]/1(强调我的)推断出来:

    一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是指向表达式指向的对象或函数的左值.

    并且[dcl.type.simple] /4.4(强调我的):

    否则,如果e是左值,则decltype(e)是T&,其中T是e的类型;

    两者都提到了工作草案.

  • 感谢@LightnessRacesInOrbit的评论.请注意,decltype(*bar)存在const foo &是一个有趣的C++怪癖decltype,因为*bar不是const foo &.

  • 值得一提的是imo:`decltype(*bar)`不是`const foo`它是`const foo&` (10认同)
  • @RyanHaining有一个简短的解释为什么`decltype(*bar)`是`const foo&`而不是`const foo`? (2认同)
  • @skypjack:是的,你的报价是正确的.我只想用简单的语言提醒每个人,取消引用指针不会产生引用;) (2认同)