问题描述
除非您将标记为 ok 的行替换为标记为失败的行,否则以下代码将按预期的方式编译和运行。唯一的不同是,我正在制作一个本地临时constexpr变量。我不明白为什么它只在一种情况下失败。
#include <array>
using TArray = std::array<int,2>;
using TSize = TArray::size_type;
constexpr TSize Fn(const TArray& myArray)
{
//constexpr TSize ret{myArray.size()}; return ret; // fails
return myArray.size(); // ok
}
int main()
{
constexpr TArray ar{};
constexpr TSize i{Fn(ar)};
return i;
}
我在gcc和c ++ 17中遇到的错误是
<source>: In function 'constexpr TSize Fn(const TArray&)':
<source>:15:39: error: 'myArray' is not a constant expression
15 | constexpr TSize ret{myArray.size()}; return ret; // fails
我想补充一点,我意识到错误状态myArray不是常量表达式。我无法将其设为常量表达式,因为它是参考参数。
我的问题是不,我如何修复代码或使其正常工作。这就是为什么编译器接受 ok 而不接受 fail 行的原因?
解决方法
出现此现象的原因是
- 出现在函数内部的表达式
myArray.size()
本身不是常量表达式 ,因为它访问的引用变量的初始化在该表达式的上下文中不可见。 li> - 表达式
Fn(ar)
是一个常数表达式,尽管其评估涉及函数调用中myArray.size()
的评估。当myArray.size()
作为{em>Fn(ar)
的一部分被评估时,参考参数的初始化是可见的(函数调用本身执行该初始化)。 - 声明变量
constexpr
时,编译器将确定该变量的初始化是否为有效的常量表达式本身; - 因此,
Fn(ar)
可用于初始化constexpr
变量,但myArray.size()
可能不会。
令人惊讶的是,有时允许常量表达式包含本身不是 常量表达式的表达式。
,如果您删除constexpr
,将会看到编译器接受失败行。 constexpr
变量必须在编译时初始化(这也意味着它是隐式静态的)。但是标记为constexpr
的函数可能在编译时被调用 ,但不是必须的。也可以在运行时调用它,然后,此初始化将不在编译时,而是在运行时。那是被禁止的。对于constexpr
函数,您应该始终考虑一下,如果删除constexpr
,它们是否也会起作用。