[英]Why is `std::array::at()` not implemented as a template function?
Edit: I forgot to mention that this would be in a constexpr
context where there aren't any dynamical indices. 编辑:我忘了提到这将在没有任何动态索引的
constexpr
上下文中。
Consider the following (very naive) implementation: 考虑以下(非常幼稚)的实现:
template <class T, std::size_t N>
class array
{
// ...
template <size_type pos>
reference at()
{
static_assert(pos < N, "Index out of range.");
return m_data[pos];
}
}
With the following usage: 具有以下用法:
int main()
{
array<int, 5> a{1, 2, 3, 4, 5};
cout << a.at(10) << "\n"; // will throw at runtime
cout << a.at<10>() << "\n"; // static assert error; index out of range
return 0;
}
This effectively prevents out of range access for the types and any nasty segfaults or exceptions that would be thrown. 这有效地防止了对类型的超范围访问以及任何可能引发的讨厌的段错误或异常。 An error would be thrown by the compiler that looks like this:
编译器将引发如下错误:
error: static assertion failed: Index out of range
static_assert(pos < N, "Index out of range");
And most IDEs would catch the erroneous access. 而且大多数IDE都会捕获错误的访问。 So why is it not implemented as such?
那么为什么不这样实现呢?
Note: I'm sorry if this is obvious but I'm planning on writing my own array
class for performance and safety, and this thought popped into my head. 注意:如果这很明显,我很抱歉,但是我正计划编写自己的
array
类以提高性能和安全性,这种想法浮现在脑海。
You need to be able to access the elements dynamically. 您需要能够动态访问元素。 Only because the size is known at compile-time does not mean that the accessed indices are known at compile-time.
仅因为在编译时知道大小,并不意味着在编译时知道访问的索引。 Consider
a.at(i)
where i
is user input. 考虑
a.at(i)
,其中i
是用户输入。
Furthermore, the at
function is required to do a runtime-check by the standard, see [sequence.reqmts] 此外,需要
at
函数按标准进行运行时检查,请参阅[sequence.reqmts]
The member function
at()
provides bounds-checked access to container elements.成员函数
at()
提供对容器元素的范围检查访问。at()
throwsout_of_range
ifn >= a.size()
.at()
抛出out_of_range
如果n >= a.size()
Also, starting with C++17 the at
member function is marked constexpr, so for an index which is constant at compile-time there is no difference to the templated function in your question. 另外,从C ++ 17开始,
at
成员函数被标记为constexpr,因此对于在编译时为常数的索引,与您问题中的模板函数没有区别。
#include <array>
int main() {
constexpr std::array<int,5> a = { 1, 2, 3, 4, 5 };
constexpr int b = a.at(2);
constexpr int c = a.at(10);
}
Live on Wandbox (Error message is meh...) Live on Wandbox (错误消息为meh ...)
If you known the index at compile-time and do not want to pay the extra cost that at
incurs before C++17, you can just use std::get<10>(a)
. 如果你知道在编译时的指数,不希望支付的额外费用
at
之前C ++ 17即被,你可以只使用std::get<10>(a)
。 This variant has zero-overhead, because the compiler can inline the array access entirely 此变体的开销为零,因为编译器可以完全内联数组访问
#include <array>
int test(std::array<int,5> const &a) {
return std::get<1>(a);
}
You can use std::get
for that purpose: 您可以使用
std::get
达到此目的:
#include <array>
#include <iostream>
int main() {
constexpr std::array<int, 4> foo { 1, 2, 3, 4 };
std::cout << std::get<2>(foo) << std::endl;
}
Also note that both operator[]
and at
are constexpr
: 还要注意,
operator[]
和at
都是constexpr
:
constexpr std::array<int, 4> foo { 1, 2, 3, 4 };
std::cout << std::get<foo[2]>(foo) << std::endl;
std::cout << std::get<foo.at(2)>(foo) << std::endl;
This evaluates to std::get<3>(foo)
(which evaluates to 4, since it's also a constexpr
). 结果为
std::get<3>(foo)
(由于它也是constexpr
,结果为4)。
Compilation already fails if you try to use an index out of the array's bounds in a constexpr
. 如果您尝试在
constexpr
使用超出数组范围的索引,则编译已失败。
Finally, as multiple people have already pointed out, one major benefit of arrays is that you can read at dynamic indices from them. 最后,正如许多人已经指出的那样,数组的主要优点是您可以从中读取动态索引。 IMO, you are overstating the usefulness of static indexing.
IMO,您高估了静态索引的有用性。
So why is it not implemented as such?
那么为什么不这样实现呢?
Because in your way can works only with values known at compile time. 因为以您的方式只能使用编译时已知的值。
I mean... with at()
defined as receiving a parameter (not template), you can write 我的意思是...将
at()
定义为接收参数(不是模板),您可以编写
for ( auto i = 0U ; i < 4U ; ++i )
std::cout << a.at(i) << std::endl;
But you can't write 但是你不能写
for ( auto i = 0U ; i < 4U ; ++i )
std::cout << a.at<i>() << std::endl;
because i
isn't known (fixed) ar compile time. 因为
i
不知道(固定)ar编译时间。
-- EDIT-- -编辑-
The OP write OP写
What I really wanted is to enforce the bound checking at compile time
我真正想要的是在编译时强制执行边界检查
If you write at()
as follows 如果您按如下方式编写
at()
constexpr T & at (std::size_t pos)
{ return pos < N ? m_data[pos] : throw std::range_error("out of range"); }
you get a bound checking compile time, when the method is constexpr
executed compile time, and run time otherwise. 当方法是
constexpr
执行的编译时,您将获得一定的检查编译时间,否则,将获得运行时间。
Obviously we wouldn't want to have only indices that are compile-time constants. 显然,我们不想只包含编译时常量的索引。 If you're asking “why not also have a function template for safe access with constant-expression indices?”, I can say only that the use case seems not very common (unlike for
std::tuple
) and that the client can implement it themselves with no difficulty or loss of performance (which is one of the usual criteria for inclusion in the standard library). 如果您问“为什么还没有功能模板来安全地使用常量表达式索引?”,我只能说用例似乎不太常见(与
std::tuple
),并且客户端可以实现它本身没有任何困难或性能下降(这是标准库中包含的通常标准之一)。
Re comments: even in a constexpr
function, you can't use a normal function parameter as a template argument. 注释:即使在
constexpr
函数中,也不能使用常规函数参数作为模板参数。
Beyond what has been pointed out about at
having to take a runtime argument, and std::get as an alternative: if you use operator[]
in a constexpr context and go out of bounds you will get a compile time error in C++14 as you seem to want, as long as you invoke it on a const array: 超出已指出大约
at
不得不采取一个运行参数,和std ::获得作为替代:如果您使用operator[]
在constexpr背景和出界,你会用C得到一个编译时错误++ 14如您所愿,只要您在const数组上调用它即可:
const std::array<int, 5> a{};
constexpr int v = a[10];
gcc gives the error: gcc给出错误:
error: array subscript value ‘10’ is outside the bounds of array type ‘std::__array_traits<int, 5>::_Type {aka int [5]}’
constexpr int v = a[10];
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.