我无法理解为什么gcc-8.2.0和clang-7.0.0都拒绝以下代码(实时代码here):
#include <array>
int main() {
constexpr std::array<int,3> v{1,2,3};
constexpr auto b = v.begin(); // error: not a constexpr
return 0;
}
有错误
error: '(std::array<int, 3>::const_pointer)(& v.std::array<int,3>::_M_elems)'
is not a constant expression (constexpr auto b = v.begin();)
根据en.cppreference.com,begin()
成员函数被声明为constexpr
。这是编译器错误吗?
答案 0 :(得分:47)
因此,让我们回避std::array
,以使其变得更容易:
template <typename T, size_t N>
struct array {
T elems[N];
constexpr T const* begin() const { return elems; }
};
void foo() {
constexpr array<int,3> v{{1, 2, 3}};
constexpr auto b = v.begin(); // error
}
constexpr array<int, 3> global_v{{1, 2, 3}};
constexpr auto global_b = global_v.begin(); // ok
为什么b
是错误的,但是global_b
可以吗?同样,如果我们将b
声明为v
,为什么static constexpr
会好起来?从根本上说,问题在于指针。为了具有作为指针的常量表达式,它必须始终指向一个已知的常量对象。没有静态存储持续时间的局部变量实际上不起作用,因为它们具有根本上可变的地址。但是对于函数局部静态变量或全局变量,它们确实具有一个常量地址,因此您可以采用指向它们的常量指针。
以标准语,来自[expr.const]/6:
常量表达式是glvalue核心常量表达式,它引用的是一个常量表达式(如下定义)的允许结果的实体,或者是其值满足以下条件的prvalue核心常量表达式:以下限制:
- 如果该值是类类型的对象,则[...]
- 如果值是指针类型,则它包含具有静态存储持续时间的对象的地址,该对象末尾的地址([expr.add]),一个函数或一个空指针值,以及
- [...]
b
不是第二个项目符号中的所有内容,因此失败。但是global_b
满足大胆的条件-如果b
被声明为v
,static
也会满足。