std::begin
和std::end
知道container
或array
的开头和结尾。
例如,很容易知道end
的{{1}}和begin
,因为它是一个提供此信息的类。但是,它如何知道vector
的结尾如下?
array
int simple_array[5]{1, 2, 3, 4, 5};
auto beg=std::begin(simple_array);
auto en=std::end(simple_array);
并不难知道数组的起始位置。但它如何知道它的结束?将常数整数std::begin
存储在某处吗?
如果我得到一些低级信息的答案,我将不胜感激。
答案 0 :(得分:25)
但是,它如何知道数组的结尾
它使用模板非类型参数来推断数组的大小,然后可以使用它来生成结束指针。来自std::end的cppreference部分的C ++ 11签名如下:
template< class T, std::size_t N >
T* end( T (&array)[N] );
正如hvd所说,由于它是通过引用传递的,因此可以防止衰减到指针。
实施类似于:
template< class T, std::size_t N >
T* end( T (&array)[N] )
{
return array + N ;
}
常数整数5是否会存储在哪里?
5
或N
是数组类型的一部分,因此N
在编译时可用。例如,将sizeof应用于数组将为我们提供数组中的总字节数。
很多时候我们看到一个数组按值传递给一个函数。在这种情况下,数组decays to a pointer要存储在数组中。所以现在大小信息丢失了。通过引用传递允许我们避免这种信息丢失并从类型中提取大小N
。
答案 1 :(得分:23)
是常量整数5将存储在哪里?
是的,它是数组类型的一部分。但不,它没有明确地存储在任何地方。当你有
int i[5] = { };
i
的类型为int[5]
。 Shafik的回答讨论了如何使用这个长度来实现end
。
如果您使用的是C ++ 11,那么使用constexpr
将是一种简单的方法
template <typename T, size_t N>
inline constexpr size_t
arrLen(const T (&arr) [N]) {
return N;
}
如果您使用constexpr
不可用的C ++ 11之前的编译器,则可能无法在编译时评估上述函数。所以在这种情况下,您可以使用:
template <typename T, size_t N>
char (&arrLenFn(const T (&arr) [N]))[N];
#define arrLen(arr) sizeof(arrLenFn(arr))
首先,我们声明一个函数返回对N char
的数组的引用,即sizeof
此函数现在将是数组的长度。然后我们用一个宏来包装它,以便它在呼叫者的最后可读。
注意:两个基本类型相同但长度不同的数组仍然是两种完全不同的类型。 int[3]
与int[2]
不同。但是,Array decay会在两种情况下都为您int*
。如果您想了解更多信息,请阅读How do I use arrays in C++?。
答案 2 :(得分:8)
因为您将数组传递给std::end
,并且数组的类型为T [N]
。 std::end
可以通过查看类型中的N
来判断数组何时结束。