constexpr函数能否返回局部对象指针?为何符合C++14要求可编译?
constexpr函数能否返回局部对象指针?
首先直接给结论:在C14标准下,你可以声明一个返回局部对象指针的constexpr函数(只要语法符合要求),但这样的函数几乎没办法在合法的常量表达式上下文里使用。我们结合你给出的代码和C14的规则来拆解:
先看C++14对constexpr函数的要求
根据标准,constexpr函数需要满足这些条件:
constexpr函数必须满足以下要求:
- 不能是虚函数
- 返回类型和所有参数必须是LiteralType
- 存在至少一组参数值,使得函数调用可作为核心常量表达式(core constant expression)的求值子表达式(构造函数只需可用于常量初始化即可),此违规无需诊断。
函数体必须是删除、默认,或仅包含除以下之外的语句:
- asm声明
- goto语句
- case和default之外的带标签语句
- try块
- 非LiteralType变量的定义
- 静态或线程存储期变量的定义
- 未初始化的变量定义
为什么你的func1能被声明为constexpr?
先看你的代码:
constexpr int * func1 (int a) { int b = 4; return &b; } int main() { constexpr int * a = func1(3); int arr[*a]; std::cout << a << std::endl; }
对照上面的规则一条条看:
func1不是虚函数:符合要求。- 返回类型
int*和参数int都是LiteralType:int是标量类型,指向标量类型的指针也属于LiteralType,满足条件。 - 函数体内容:只定义了一个初始化过的
int变量(属于LiteralType),然后返回它的地址,没有使用任何被禁止的语句,语法完全合规。
至于标准里提到的“存在至少一组参数让函数调用成为核心常量表达式”,标准明确说了这个违规不需要编译器诊断——也就是说,哪怕你的func1实际上没有任何参数能让它在编译期合法求值,编译器也不会因此拒绝它作为constexpr函数的声明。
关于“编译时获知局部变量地址”的误区
这里你可能被编译器的行为误导了:
- 本质上,编译期根本无法获知局部变量的地址。局部变量属于自动存储期,每次函数调用时才会在栈上分配内存,栈的位置在运行时才确定,编译期不可能提前知道它的地址。
- 你的代码里
constexpr int *a = func1(3);其实是不符合标准的——因为func1(3)返回的是局部变量b的地址,这不属于核心常量表达式,不能用来初始化constexpr变量。但GCC 6.4.0可能做了扩展,或者没有严格执行这个标准检查,允许代码编译。 - 你代码里的
int arr[*a];能编译,是因为GCC支持C99的变长数组(VLA)作为扩展,这里*a是运行时的值,数组大小在运行时确定,并不是真的在编译期用常量表达式计算出来的。
简单来说:func1能被声明为constexpr是因为它符合语法要求,但它并不能真正在编译期求值返回合法的常量地址,你看到的编译通过其实是编译器的非标准行为。
内容的提问来源于stack exchange,提问作者code707
相关产品推荐
相关产品推荐

