简体   繁体   English

为什么`std :: array :: at()`没有实现为模板函数?

[英]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() throws out_of_range if n >= 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);
}

Live on Godbolt 活在戈尔波特上

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM