为什么我可以在 constexpr 函数中修改 const_cast-ed 对象?
Why can I modify const_cast-ed object in constexpr function?
我一直假设:
- 写入
const_cast
ed 变量是 UB
- constexpr 中不允许有 UB
所以我很困惑为什么这个代码 compiles:
constexpr int fn(){
int v = 42;
return [v]() {
const_cast<int&>(v)+=5;
return v;
}();
}
static constexpr auto val = fn();
int main() {
return val;
}
注意:我知道没有理由不允许这样做,因为结果应该是显而易见的,我更感兴趣的是为什么允许这样做的法律解释。
这部分是真的:
there is no UB allowed in constexpr
这部分不是:
writing to const_cast
-ed variable is UB
实际规则是,来自[dcl.type.cv]/4:
Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.
请注意,相关部分是 对象 是否为 const
- 而不是您到达它所采取的任何路径是否为 const
.在这种情况下,v
不是 const
对象,也不是在复制时在 lambda 中创建的对象。
但是,如果您将 v
声明为 const
,那么在 lambda 中声明的那个也将被声明为 const
,因此修改它的尝试将是未定义的行为。因此,您的代码将无法编译。
lambda 表达式转换为类似于此的内容:
struct unnamed {
int v;
int operator()() const {
const_cast<int&>(v)+=5;
return v;
}
};
没有 const_cast
你不能在 operator()
中修改 v
因为运算符是 const
方法,但 v
本身不是 const
.
情况与
相同
struct foo {
int x = 0;
void operator() const {
const_cast<int&>(x) += 42;
}
};
那么这是“好的”:
foo f;
f();
虽然这是未定义的:
const foo f;
f();
我一直假设:
- 写入
const_cast
ed 变量是 UB - constexpr 中不允许有 UB
所以我很困惑为什么这个代码 compiles:
constexpr int fn(){
int v = 42;
return [v]() {
const_cast<int&>(v)+=5;
return v;
}();
}
static constexpr auto val = fn();
int main() {
return val;
}
注意:我知道没有理由不允许这样做,因为结果应该是显而易见的,我更感兴趣的是为什么允许这样做的法律解释。
这部分是真的:
there is no UB allowed in constexpr
这部分不是:
writing to
const_cast
-ed variable is UB
实际规则是,来自[dcl.type.cv]/4:
Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.
请注意,相关部分是 对象 是否为 const
- 而不是您到达它所采取的任何路径是否为 const
.在这种情况下,v
不是 const
对象,也不是在复制时在 lambda 中创建的对象。
但是,如果您将 v
声明为 const
,那么在 lambda 中声明的那个也将被声明为 const
,因此修改它的尝试将是未定义的行为。因此,您的代码将无法编译。
lambda 表达式转换为类似于此的内容:
struct unnamed {
int v;
int operator()() const {
const_cast<int&>(v)+=5;
return v;
}
};
没有 const_cast
你不能在 operator()
中修改 v
因为运算符是 const
方法,但 v
本身不是 const
.
情况与
相同struct foo {
int x = 0;
void operator() const {
const_cast<int&>(x) += 42;
}
};
那么这是“好的”:
foo f;
f();
虽然这是未定义的:
const foo f;
f();