关于在需要常量表达式的上下文中使用的glvalue常量表达式的问题
The question about a glvalue constant expression used in a context that requires a constant expression
#include <iostream>
constexpr int func2(int const& id){
return id;
}
template<int v>
struct Test{
};
int main(){
const int v = 0;
Test<func2(v)> c;
}
考虑上面的代码,我只是不明白为什么代码格式很好。我的观点是,当求值表达式func2
时,名称v
被用作glvalue,因为func2
的参数是引用类型的,所以v
需要绑定到id表达式id
。所以我们来看看glvalue常量表达式的要求,这里有关于这一点的引号。
常量表达式是指作为常量表达式(如下定义)的允许结果的实体的glvalue核心常量表达式,或者是其值满足以下约束的prvalue核心常量表达。
我们忽略prvalue的情况,因为这里v
被用作glvalue。
一个实体是常量表达式的允许结果,它是:
如果实体是具有静态存储持续时间的对象,而该对象不是临时对象,或者是值满足上述约束的临时对象,或是函数,则该实体是常量表达式的允许结果。
在我的程序部分中,const int v = 0;
没有静态存储持续时间,它只有自动存储持续时间。因此,当对表达式func2(v)
求值以确定它是否是常数表达式时,首先,v
必须是glvalue核心常数表达式,它指的是作为常数表达式的允许结果的实体,因此,为什么程序在这里格式良好?如果我丢失了任何重要报价,请更正。
我们忽略prvalue的情况,因为这里v被用作glvalue
是吗?这是cppreference中的一个例子:
void test() {
static const int a = std::random_device{}();
constexpr const int& ra = a; // OK: a is a glvalue constant expression
constexpr int ia = a; // Error: a is not a prvalue constant expression
const int b = 42;
constexpr const int& rb = b; // Error: b is not a glvalue constant expression
constexpr int ib = b; // OK: b is a prvalue constant expression
}
是的,const int b = 42
在这里相当奇怪,因为从技术上讲,你可以将b
绑定到const int&
,const_cast
和const
,并为其分配一个运行时值。然而,考虑到什么是积分常数表达式,const
对象的要求是什么,这是完全合理的:
积分常数表达式是积分或未缩放的表达式枚举类型隐式转换为prvalue,其中转换表达式是一个核心常量表达式。如果表达式在整型常量表达式为应为,表达式在上下文中隐式转换为整型或无范围枚举类型。
变量b
看起来确实像是可以隐式转换为prvalue常量表达式的东西,因为它在该上下文中基本上充当文本42
的别名,而整数文本根据定义是prvalue。
现在对于有问题的部分-这个:
const对象-类型为const限定的对象,或const对象的不可变子对象。这样的对象不能修改:尝试直接这样做是一个编译时错误,并且尝试间接地(例如,通过修改const对象通过指向非常量类型的引用或指针)导致未定义行为
和:
核心常量表达式是任何求值为不评估以下任何一项:
一种表达式,其评估可导致任何形式的核心语言未定义的行为(包括有符号整数溢出、除以零、数组边界外的指针算术等)。是否标准检测到库未定义的行为是未指定的。
意味着一旦你开始用b
做有趣的事情,你就可以期待任何事情的发生。例如,这就是我在最新的MSVC中尝试对您的代码所做的,所有标准一致性选项都打开了:
#include <iostream>
#include <random>
constexpr int func2(int const& id) {
return id;
}
template<int v>
struct Test {
long array[v];
};
int main() {
const int v = 0;
const int& ref = v;
const_cast<int&>(ref) = std::random_device()() % std::numeric_limits<int>::max();
Test<func2(v)> c;
return 0;
}
打开语言扩展后,我得到了一个C4200:使用了非标准扩展:结构/联合警告中的零大小数组。关闭后,程序将无法编译。当我从结构中删除array
部分时,它又开始编译了。
我试图回答这个问题。为什么func2(v)
是一个常量表达式,因为对于表达式func2(v),当评估这个后缀表达式时,不要求v
必须是"将评估以下表达式之一"列表中的glvalue常量表达式:",Even,这些规则并不要求潜在核心常量表达式中的一个表达式是glvalue常量表达式,只要求该表达式不违反列出的要求。让我们继续,当初始化参数时,这里有另一条规则:
完整的表达式是:
- […]
- init声明符或mem初始化器,包括初始化器的组成表达式
所以,当对这个完整表达式求值时,它只会不违反这些列出的条件,那么
func2(v)
就会被求值为一个常量表达式,所以让我们看看这些规则:一个id表达式,它引用引用类型的变量或数据成员,除非该引用具有先前的初始化和
- 它是用常量表达式初始化的,或者
- 其寿命始于e的评价
对于id表达式
id
,其前面的初始化是相应的参数,因为这个规则:调用函数时,每个参数([dcl.fct])都应初始化([dcl.init]、[class.copy]、[cclass.ctor])及其相应的参数。
因此,第一个条件为true。"它是用常量表达式初始化的"为假,条件"它的生存期在e的求值范围内开始"为真。结论
func2(v)
表达确实是的恒定表达
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 使用自动推导的 lambda 参数作为常量表达式
- 生成提升::hana::set 的常量表达式问题
- 为什么不能用常量表达式声明数组?
- 不是 lambda 函数中的常量表达式
- 函数调用在常量表达式中必须具有常量值
- 错误:constexpr 变量'struct2Var'必须由常量表达式初始化
- 关于在需要常量表达式的上下文中使用的glvalue常量表达式的问题
- 生成 constexpr 字符串表,不能产生常量表达式
- 整体模板参数。错误:在常量表达式中使用'this'
- 如何在满足常量表达式的同时将整数传递给指针,传递给 std::array<double、integer>?
- 编译器错误:函数调用在常量表达式中必须有一个常量值
- 错误:'new'不能出现在常量表达式中
- 我可以写出小于 -0.5 两个 ulps 的常量表达式双精度吗?
- 编译器在传递 const 变量时返回错误:模板参数不是常量表达式
- 为什么我不能在非常量表达式上使用此模板阶乘函数?
- C++ 使用变量而不是常量表达式初始化数组
- 使用函数参数作为常量表达式的一部分 - gcc vs clang
- 片段着色器中的"错误:在 GLSL 1.30 及更高版本中禁止使用非常量表达式索引的采样器数组"