【问题标题】:constexpr begin of a std::arraystd::array 的 constexpr 开头
【发布时间】:2018-10-24 15:15:20
【问题描述】:

我无法理解为什么 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。这是编译器错误吗?

【问题讨论】:

  • 使用.cbegin .
  • @АлексейНеудачин 没有区别。
  • @АлексейНеудачин begin()cbegin() 在 const 对象上做同样的事情。
  • 所以尝试auto b=...,如果失败则尝试const。你也把auto改成std::....::citerator

标签: c++ c++17 constexpr


【解决方案1】:

所以让我们避开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 没问题?同样,如果我们将v 声明为static constexpr,为什么b 会变得正常?问题基本上与指针有关。为了拥有一个作为指针的常量表达式,它必须始终指向一个已知的常量。对于没有静态存储持续时间的局部变量,这实际上并不适用,因为它们的地址基本上是可变的。但是对于函数局部静态或全局,它们确实有一个常量地址,因此您可以使用一个常量指针来指向它们。


标准语,来自[expr.const]/6

常量表达式要么是一个泛左值核心常量表达式,它引用一个作为常量表达式(如下定义)的允许结果的实体,要么是一个纯右值核心常量表达式,其值满足以下约束:

  • 如果值是类类型的对象,[...]
  • 如果值是指针类型,它包含具有静态存储持续时间的对象的地址、经过此类对象末尾的地址 ([expr.add])、一个函数,或者一个空指针值,以及
  • [...]

b 不是第二个项目符号中的那些东西,所以它失败了。但是global_b 满足粗体条件 - 如果v 被声明为staticb 也会满足。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-22
    • 2019-01-31
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    • 2015-10-19
    • 2012-12-25
    • 1970-01-01
    相关资源
    最近更新 更多