我正在研究核心常量表达式*中允许的内容,这在C++标准草案的5.19 常量表达式第2段中有所描述:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是未评估的逻辑AND(5.14),逻辑OR(5.15)和条件(5.16)操作的子表达式不考虑[注意:重载的运算符调用函数.-end note]:
并列出随后的子弹中的排除项并包括(强调我的):
- 具有未定义行为的操作 [注意:包括,例如,有符号整数溢出(第5条),某些指针算术(5.7),除零(5.6)或某些移位操作(5.8) - 结束注释];
嗯?为什么常量表达式需要此子句来涵盖未定义的行为?常量表达式是否有一些特殊的东西需要未定义的行为才能在排除中进行特殊划分?
拥有这个条款是否给了我们没有它的任何优势或工具?
作为参考,这看起来像广义常量表达式提案的最新修订版.
我们都听过警告,如果你在C或C++中调用未定义的行为,任何事情都可能发生.
这是否仅限于任何运行时行为,还是包括任何编译时行为?特别是,编译器在遇到调用未定义行为的构造时,允许拒绝代码(在标准中没有其他要求的情况下这样做),甚至崩溃?
const 向量无法修改,因为它是 const 对象。所以插入、追加、删除都是不允许的。然而,它的内容不是该对象的一部分,而是由该对象拥有。举个类似的例子:
int* const p = new int[10]{1,2,3,4};
Run Code Online (Sandbox Code Playgroud)
p是一个 const 对象,拥有可以修改的非常量数据:p[1]=5;
Vector 的operator[]条件是向量是否为 const,如果是,则返回 aconst int&但如果基础值不是 const,则删除 const 的 const 强制转换应该是合法的。
为了测试这一点,我编写了以下程序:
#include <vector>
constexpr int foo()
{
const std::vector<int> v{ 1,2,3 };
const int a[3]{ 1,2,3 };
*const_cast<int*>(&v[1]) = 21;
// However, this should fail and does on GCC and CLANG
//*const_cast<int*>(&a[1]) = 21;
return v[1];
}
int main()
{
constexpr int sb21 = foo();
const std::vector<int> v{ 1,2,3 };
*const_cast<int*>(&v[1]) …Run Code Online (Sandbox Code Playgroud) 基于以下两条规则:
那么为什么编译器不抱怨这个呢?
constexpr int _UB() {return 1;}
int main() {
constexpr int a = _UB();
return a;
}
Run Code Online (Sandbox Code Playgroud)
另外,我看到很多专业的、符合 MISRA 的代码似乎违反了此命名规则,请参见此处:
#ifndef __STM32F732xx_H
#define __STM32F732xx_H
Run Code Online (Sandbox Code Playgroud)
所有这些库都调用 UB 吗?
可以比较指向类成员变量的指针,结果取决于声明顺序。请参阅规范例如,编译器资源管理器中的此示例有效并返回 true (1):
struct A {
int a0 = 1;
int a1 = 2;
};
consteval int foo()
{
A a;
int* p1 = &a.a0;
int* p2 = &a.a1;
return p2 > p1;
}
int main()
{
return foo();
}
Run Code Online (Sandbox Code Playgroud)
因此,人们会期望它p2-p1会返回指针之间的距离(以 int 对象为单位)。它在运行时执行。编译器浏览器
struct A {
int a0 = 1;
int a1 = 2;
};
int foo()
{
A a;
int* p1 = &a.a0;
int* p2 = &a.a1;
return p2 - p1;
} …Run Code Online (Sandbox Code Playgroud)