为什么 std::array<T,N>::begin() 自 C++17 以来是一个 constexpr?
Why is std::array<T,N>::begin() a constexpr since C++17?
从 C++17 开始,std::array<T,N>::begin()
是 constexpr:
constexpr iterator begin() noexcept;
但是begin
的return怎么可能在编译时就知道呢?例如:
int main() {
auto p = std::make_unique<std::array<int,2>>();
auto it = p->begin();
}
是完全合法的代码(虽然可能有点没用)。底层数组的开始以及迭代器取决于 malloc 的地址。
我觉得我对 constexpr
的作用有误解,因为我不明白 any 非静态成员函数可能是 constexpr
,尤其是当它(传递地)访问数据成员时。
constexpr
函数可以在非编译时常量表达式中调用。此类调用在运行时进行评估。只有在编译时常量表达式中调用 constexpr
函数时,才会在编译时计算函数。
But how can the return of begin be known at compile time?
当数组本身是一个编译时常量时,在编译时就可以知道
当数组不是编译时常量时在编译时无法获知这一事实不是问题,因为在这种情况下函数是在运行时执行的。
您不需要唯一指针来查看编译时与运行时评估的效果。这确实编译:
#include <array>
int main() {
static constexpr std::array<int,2> p{1,2};
constexpr auto it = p.begin();
}
但这不是:
#include <array>
int main() {
std::array<int,2> p{1,2};
constexpr auto it = p.begin();
}
错误:
<source>:5:18: error: constexpr variable 'it' must be initialized by a constant expression
constexpr auto it = p.begin();
^ ~~~~~~~~~
<source>:5:18: note: pointer to subobject of 'p' is not a constant expression
<source>:4:21: note: declared here
std::array<int,2> p{1,2};
马虎地说,begin()
的constexpr
意味着可以在constexpr
对象上在编译时评估该方法。对于非 constexpr
数组,该方法在运行时计算。因此 p.begin()
不能用于初始化 constexpr it
.
此更改解决的问题是能够在 constexpr
函数中使用 non-const
std::array
来计算某些结果。回想一下,constexpr
函数在编译时计算时不能调用非 constexpr
函数。
作为一个例子,考虑一个计算 i = 1 到 N 之和的函数。为了论证,考虑一个非常愚蠢的方法(现实世界中存在非愚蠢的例子,但比this): 创建一个用 {1, 2, 3, ...}
初始化的数组,然后返回 array
.
的元素之和
// Compute sum from i = 1 to N
template <unsigned N>
unsigned
sum() noexcept
{
std::array<unsigned, N> a{};
unsigned u = 1;
for (auto i = a.begin(); i != a.end(); ++i, ++u)
*i = u;
u = 0;
for (auto const x : a)
u += x;
return u;
}
这很好用,可以这样调用:
auto x = sum<5>(); // x == 15
现在有人说:嘿,让我们在编译时计算这个!
在 C++17 中,这就像将 constexpr
打到 sum
和 x
:
上一样简单
// Compute sum from i = 1 to N
template <unsigned N>
constexpr
unsigned
sum() noexcept
...
constexpr auto x = sum<5>(); // x == 15
但是在 C++14 中这不能编译:
test.cpp:24:20: error: constexpr variable 'x' must be initialized by a constant expression
constexpr auto x = sum<5>();
^ ~~~~~~~~
test.cpp:11:21: note: non-constexpr function 'begin' cannot be used in a constant expression
for (auto i = a.begin(); i != a.end(); ++i, ++u)
^
它无法编译的原因是 array<T, N>::begin()
不是 constexpr
函数。
从 C++17 开始,std::array<T,N>::begin()
是 constexpr:
constexpr iterator begin() noexcept;
但是begin
的return怎么可能在编译时就知道呢?例如:
int main() {
auto p = std::make_unique<std::array<int,2>>();
auto it = p->begin();
}
是完全合法的代码(虽然可能有点没用)。底层数组的开始以及迭代器取决于 malloc 的地址。
我觉得我对 constexpr
的作用有误解,因为我不明白 any 非静态成员函数可能是 constexpr
,尤其是当它(传递地)访问数据成员时。
constexpr
函数可以在非编译时常量表达式中调用。此类调用在运行时进行评估。只有在编译时常量表达式中调用 constexpr
函数时,才会在编译时计算函数。
But how can the return of begin be known at compile time?
当数组本身是一个编译时常量时,在编译时就可以知道
当数组不是编译时常量时在编译时无法获知这一事实不是问题,因为在这种情况下函数是在运行时执行的。
您不需要唯一指针来查看编译时与运行时评估的效果。这确实编译:
#include <array>
int main() {
static constexpr std::array<int,2> p{1,2};
constexpr auto it = p.begin();
}
但这不是:
#include <array>
int main() {
std::array<int,2> p{1,2};
constexpr auto it = p.begin();
}
错误:
<source>:5:18: error: constexpr variable 'it' must be initialized by a constant expression
constexpr auto it = p.begin();
^ ~~~~~~~~~
<source>:5:18: note: pointer to subobject of 'p' is not a constant expression
<source>:4:21: note: declared here
std::array<int,2> p{1,2};
马虎地说,begin()
的constexpr
意味着可以在constexpr
对象上在编译时评估该方法。对于非 constexpr
数组,该方法在运行时计算。因此 p.begin()
不能用于初始化 constexpr it
.
此更改解决的问题是能够在 constexpr
函数中使用 non-const
std::array
来计算某些结果。回想一下,constexpr
函数在编译时计算时不能调用非 constexpr
函数。
作为一个例子,考虑一个计算 i = 1 到 N 之和的函数。为了论证,考虑一个非常愚蠢的方法(现实世界中存在非愚蠢的例子,但比this): 创建一个用 {1, 2, 3, ...}
初始化的数组,然后返回 array
.
// Compute sum from i = 1 to N
template <unsigned N>
unsigned
sum() noexcept
{
std::array<unsigned, N> a{};
unsigned u = 1;
for (auto i = a.begin(); i != a.end(); ++i, ++u)
*i = u;
u = 0;
for (auto const x : a)
u += x;
return u;
}
这很好用,可以这样调用:
auto x = sum<5>(); // x == 15
现在有人说:嘿,让我们在编译时计算这个!
在 C++17 中,这就像将 constexpr
打到 sum
和 x
:
// Compute sum from i = 1 to N
template <unsigned N>
constexpr
unsigned
sum() noexcept
...
constexpr auto x = sum<5>(); // x == 15
但是在 C++14 中这不能编译:
test.cpp:24:20: error: constexpr variable 'x' must be initialized by a constant expression
constexpr auto x = sum<5>();
^ ~~~~~~~~
test.cpp:11:21: note: non-constexpr function 'begin' cannot be used in a constant expression
for (auto i = a.begin(); i != a.end(); ++i, ++u)
^
它无法编译的原因是 array<T, N>::begin()
不是 constexpr
函数。