constexpr std :: array的开头

时间:2018-10-24 15:15:20

标签: c++ c++17 constexpr

我无法理解为什么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.combegin()成员函数被声明为constexpr。这是编译器错误吗?

1 个答案:

答案 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被声明为vstatic也会满足。