我不明白为什么std::begin()
函数在被赋予int * arr
指针的情况下不起作用,但是可以与int arr[]
数组一起工作。
此代码无效:
int *arr = new int[5]{ 1,2,3,4,5 };
if (find(begin(arr),end(arr),5)!=end(arr))
{
cout << "found";
}
此代码确实有效:
int arr2[5] = { 1,2,3,4,5 };
if (find(begin(arr2),end(arr2),5)!=end(arr2))
{
cout << "found";
}
答案 0 :(得分:6)
它不起作用,因为int *arr
不是数组,而是指针。它碰巧指向数组的第一个元素,但指向该元素,而不是数组。一旦将其转换为指针,长度信息就会丢失。
请注意:它不会完全丢失,因为它实际上是以特定于实现的方式隐藏的,以便分配器知道以后delete[]
可以释放多少,但这并不是暴露于除分配器之外的任何东西,由于分配器对齐要求,它通常不等于请求的实际大小,因此在这里没有用。
答案 1 :(得分:1)
std::(c)begin()
和std:(c)end()
被显式重载以使用固定大小的数组类型。关于数组大小的信息不会丢失,因为大小是数组类型本身的一部分。这些重载的实现可能看起来像这样:
template<typename T, size_t N>
T* begin(T (&arr)[N])
{
return arr;
}
template<typename T, size_t N>
const T* cbegin(const T (&arr)[N])
{
return arr;
}
template<typename T, size_t N>
T* end(T (&arr)[N])
{
return arr + N;
}
template<typename T, size_t N>
const T* cend(const T (&arr)[N])
{
return arr + N;
}
因此,std::begin(arr2)
和std::end(arr2)
仅在arr2
是int[N]
固定大小的数组类型时才完全有效。编译器可以根据传入的固定数组的类型推导T
和N
模板参数的值,例如:
int arr2[5] = { 1,2,3,4,5 };
if (find(
begin(arr2), // deduces T=int, N=5, thus calls std::begin<int,5>(arr2)
end(arr2), // deduces T=int, N=5, thus calls std::end<int,5>(arr2)
5)
!= end(arr2) // deduces T=int, N=5, thus calls std::end<int,5>(arr2)
)
{
cout << "found";
}
相反,因为int*
指针根本无法提供类似的 1参数重载,因为没有有关所指向数组大小的信息(甚至std::end()
是否指向数组)以将有效的迭代器返回到数组的末尾:
template<typename T>
T* begin(T *arr)
{
return arr; // OK
}
template<typename T>
const T* cbegin(const T *arr)
{
return arr; // OK
}
template<typename T>
T* end(T *arr)
{
return arr + N; // NOT OK, WHAT IS N SUPPOSED TO BE?!?
}
template<typename T>
const T* cend(const T *arr)
{
return arr + N; // NOT OK, WHAT IS N SUPPOSED TO BE?!?
}
但是,您可以提供自己的 2参数重载 1 ,因此您可以显式传递{{ 1}},例如:
N
namespace std
{
template<typename T>
T* begin(T *arr, size_t N)
{
return arr;
}
template<typename T>
const T* cbegin(const T *arr, size_t N)
{
return arr;
}
template<typename T>
T* end(T *arr, size_t N)
{
return arr + N;
}
template<typename T>
const T* cend(const T *arr, size_t N)
{
return arr + N;
}
}
1:不允许向int *arr = new int[5]{ 1,2,3,4,5 };
if (find(
begin(arr,5), // deduces T=int, explicit N=5, thus calls std::begin<int>(arr2,5)
end(arr2,5), // deduces T=int, explicit N=5, thus calls std::end<int>(arr2,5)
5)
!= end(arr2,5) // deduces T=int, explicit N=5, thus calls std::end<int>(arr2,5)
)
{
cout << "found";
}
名称空间添加新功能,但允许为现有标准功能添加自定义重载。 std
是一个常见的例子,尽管通常使用ADL比扩展std::swap()
命名空间更好。
答案 2 :(得分:0)
template< class C > auto begin( C& c ) -> decltype(c.begin());
将迭代器返回给定的容器c或数组的开头。这些模板依赖于C :: begin()具有合理的实现。
int *arr
不是数组,而是指针,而int arr2[5]
是数组。