对于常量表达式,为什么我不能像标准所说的那样使用指针类型对象?

abs*_*suu 1 c++ language-lawyer constant-expression constexpr

我试图找出限制constexprcpp11/14 中的限制。我在CPP14-5.19-4中发现了一些使用要求:

\n
\n

常量表达式可以是泛左值核心常量表达式\n其值引用具有静态存储持续时间的对象或函数\n,也可以是纯右值核心常量表达式\n其值是对象\n其中,对于该对象及其子对象:

\n
    \n
  • ...
  • \n
  • 如果对象或子对象是指针类型,则它包含具有静态存储持续时间的另一个对象的地址、此类对象末尾的地址 (5.7)、函数的地址或空指针值
  • \n
\n
\n

我已经对涉及地址运算符的表达式运行了一些测试(代码如下所示)&,以确保上面引用的标准语句的正确性。

\n

简而言之,我试图获取全局int变量的地址global_var,它是一个具有静态存储持续时间的对象(如果我没有想错的话),一切都按照标准指出的那样工作。但是,令我困惑的是,当我尝试分配另一个指针类型对象(global_var_addr1在代码中)时,它存储了同一对象的地址global_var时,程序将无法编译。海湾合作委员会说:

\n
\n

错误:\xe2\x80\x98global_var_addr1\xe2\x80\x99 的值在常量表达式中不可用
\n注意:\xe2\x80\x98global_var_addr1\xe2\x80\x99 未声明\xe2\x80\x98constexpr\xe2 \x80\x99

\n
\n

,而 Clang-Tidy 说:

\n
\n

错误:constexpr 变量“x2”必须由常量表达式初始化 [clang-diagnostic-error]
\n注意:在常量表达式中不允许读取非 constexpr 变量 \'global_var_addr1\'

\n
\n

我不知道为什么,我错过了什么吗?

\n

所以我的问题是:

\n

1. 为什么在常量表达式中,我不能使用标准规定的包含具有静态存储持续时间的对象的地址的指针类型对象?
\n 2. 为什么在与 (1) 相同的上下文中,当对象是auto

\n

欢迎任何建议,提前致谢!

\n
\n

代码:

\n
const int global_var_c = 123;\nint global_var = 123;\nconst void *global_var_addr1 = &global_var;\nconst void *global_var_addr2 = nullptr;\nauto global_var_addr3 = nullptr;\n\nauto  main() -> int\n{\n    constexpr const int x00 = global_var_c;           // OK\n    constexpr const void *x0 = &global_var;           // OK\n\n    // Operate on the object of pointer type\n    constexpr const void *x1 = &global_var_addr1;      // OK\n    constexpr const void *x2 = global_var_addr1;       // ERROR: read of non-constexpr variable \'global_var_addr1\'...\n\n    // Operate on nullptr\n    constexpr const void *x3 = &global_var_addr2;     // OK\n    constexpr const void *x4 = global_var_addr2;      // ERROR: read of non-constexpr variable \'global_var_addr2\'...\n\n    // Operate on nullptr (with type deduction)\n    constexpr const void *x5 = global_var_addr3;      // OK\n    constexpr const void *x6 = &global_var_addr3;     // OK\n}\n
Run Code Online (Sandbox Code Playgroud)\n

use*_*522 5

同时

constexpr const void *x2 = global_var_addr1;
Run Code Online (Sandbox Code Playgroud)

constexpr const void *x4 = global_var_addr2;
Run Code Online (Sandbox Code Playgroud)

左值到右值的转换发生在从变量global_var_addr1/global_var_addr2左值到它们所持有的指针值的过程中。仅当变量的生命周期在常量表达式求值期间开始(此处不是这种情况)或者变量可在常量表达式中使用时才允许进行此类转换,这意味着它是constexpr(此处不是这种情况)或由常量表达式初始化(是这里的情况)引用或const限定的整数/枚举类型(不是这里的情况)。

因此初始化器不是常量表达式。


这在以下情况有所不同

constexpr const int x00 = global_var_c;
Run Code Online (Sandbox Code Playgroud)

因为global_var_cconst- 限定的整数类型。


我不太确定

constexpr const void *x5 = global_var_addr3;      // OK
Run Code Online (Sandbox Code Playgroud)

直观上它应该可以工作,因为is的类型nullptr以及由此推导的类型不需要携带任何状态,因此不需要左值到右值的转换。我目前还不确定该标准是否真的保证了这一点。global_var_addr3std::nullptr_t

阅读当前的措辞(C++20 后的草案),[conv.ptr] 仅指定空指针常量(即 的纯右值)到另一种指针类型的转换,std::nullptr_t[conv.lval] 具体说明了左值如何转换为-rvalue 转换std::nullptr_t产生一个空指针常量。[conv.lval] 还在注释中澄清,此转换不会访问内存,但我认为这并不意味着它不是左值到右值的转换,因为它仍然写在该标题下。

所以在我看来,严格阅读标准

constexpr const void *x5 = global_var_addr3;      // OK
Run Code Online (Sandbox Code Playgroud)

应该是格式错误的(无论global_var_addr3是否const合格)。

是一个针对此的开放式 clang 错误。标准委员会似乎有一个内部讨论的链接,但我无法访问。


无论如何,auto占位符并不重要。你可以直接写std::nullptr_t它。

所有这些都是成为核心常量表达式的要求,这是您在问题中提到的要求的先决条件。