【问题标题】:Using iterators on arrays在数组上使用迭代器
【发布时间】:2014-06-25 22:27:18
【问题描述】:

C++ Primer 中声明

在 C++ 中,指针和数组紧密相连。特别是,作为 我们会看到,当我们使用数组时,编译器通常会将 指向指针的数组

我想使用迭代器来打印数组。下面的程序运行良好,但是当我尝试打印arr2arr3 时,如果我没记错,它是int * 类型,我得到一个错误(判断& 运算符表示下面的参考)。

error: no matching function for call to ‘begin(int*&)’

int main(int argc, char** argv) {

    int arr[] = {0,1,2,3,4,5,6,7,8,9};
    auto arr2 = arr;
    auto arr3(arr);   // I think arr2 and arr3 are of same type

    for(auto it = std::begin(arr) ; it != std::end(arr) ; ++it)
        std::cout << *it << " ";
    std::cout << std::endl;

    return 0;
}

考虑到语句,如果一个数组被编译器转换为一个指针,这个程序如何使用std::begin()std::end()打印arr的内容,而不是arr2或@987654331 @如果它们都是指向整数的指针?


编辑

如果我不能说清楚,我很抱歉。我希望我能通过这个编辑来澄清这个问题。

现在我知道begin()end() 不适用于指针(感谢答案),我想知道引用的文本是否不正确,因为它指定有一个 Array - > 指针 转换。如果文本所说的是真的,那么arr 的类型应该是一个指针。此时引用的文字有问题吗?

另外,有什么方法可以使用begin()end() 指定大小的指针(不是STL 容器),可能使用以下构造函数?

template< class T, size_t N > 
T* begin( T (&array)[N] );

【问题讨论】:

  • 至于在指针上使用begin()/end():你不能,但是你可以使用指针本身作为范围开始和指针+大小作为结束。示例:std::sort(yourPointer, yourPointer + arraySize)

标签: c++ arrays pointers c++11 iterator


【解决方案1】:

数组很容易转换为指针,但并非总是如此。例如,如果您获取数组的地址或获取引用,则原始数组类型不会丢失:

int a[10];
int (&ar)[10] = a; // fine
int (*ap)[10] = &a; // also fine

但是,当您以复制大多数其他类型的方式使用数组时,数组将转换为指针并复制指针。

在您的示例中,如果您将其作为参考,则可以使用 arr2:

 auto &arr2 = arr;

现在arr2 的类型为int (&amp;)[10] 而不是int *

【讨论】:

  • 谢谢,这正好回答了我的实际问题。 +1
【解决方案2】:

std::beginstd::end 使用 C 样式数组的参数。

std::end 的可能实现是:

template<class T, std::size_t sizeOfArray>
constexpr T *end(T (&array)[sizeOfArray])
{
  return &array[sizeOfArray];
}

这种方式arr 不会在您调用std::end(arr) 时转换为指针(并且有关数组大小的信息不会丢失......该函数只接受具有精确sizeOfArray 元素的数组作为参数)。

template<class T>
T *end(T array[])
{
  // ?
}

由于T array[] 用作函数参数时,它的行为类似于平面指针 (T *array),而不是真正的数组,因此不起作用。

auto 将数组arr 衰减为指针,技巧 将不再起作用。

【讨论】:

    【解决方案3】:

    这里,auto 会将arr(数组类型)衰减为一个指针。而std::beginstd::end 都只能用于容器或数组,不能用于指针。

    来自 C++11 标准

    §7.1.6.4 自动说明符 [dcl.spec.auto]

    1 auto 类型说明符表示被声明的变量的类型应该从它的初始化器中推导出来 或者函数声明器应包含尾随返回类型。

    你的代码不能在这里工作,因为auto 不能推断数组类型。就像:

    char a[5];
    auto b[5] = a;  // error, a evaluates to a pointer, which does
                    // not match the array type
    

    要使其工作,只需使用 C++ 容器,如 std::vector:

    vector<int> arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    

    【讨论】:

    • 我明白了。我不明白的是文本说“当我们使用数组时,编译器通常会将数组转换为指针。”所以如果arr 被转换为指针,begin() 如何和end() 工作?文本所说的是否不正确,这意味着数组不一定转换为指针,因为 begin()end() 适用于容器而不是指针?另外,是否有任何方法可以像指定大小一样使用迭代器打印arr2arr3
    【解决方案4】:

    因为std::begin 是为数组定义的,而不是为指针定义的。数组类型与指针类型不同。

    这里的问题是auto arr2 = arr 显然将类型从数组类型降级为指针类型。

    请注意,问题实际上出在std::end 而不是std::begin。毕竟,当 std::end 所拥有的只是指向第一个元素的指针时,它如何能够将指针指向最后一个元素?它不能,因此不能为指针类型定义std::end,因此std::begin 对指针类型没有任何意义。

    准确地说arr的类型是int[10],而arr2arr3的类型是int*。前者可以退化为后者,但反之则不然。

    【讨论】:

    • 这是否意味着引用的文本不一定正确,意味着编译器不会将数组转换为指针?
    • 不,引用的文字是真实的。语法arr[b] 仅表示arr + b,其中 arr 衰减为指向第一个元素的指针,而 b 是整数索引,适用普通指针算术。事实上,纯粹看语言定义 arr[b] 实际上等于 b[arr] 甚至可以工作! (至少在 C 中,我不确定这是否在 C++ 中维护)无论如何,无论何时使用数组,它几乎总是立即衰减为指针,但并非在所有情况下,如“通常”一词所示引用的文本。一个特定的例子是模板,例如std::begin
    • 至于对您问题的编辑,基本上std::beginstd::end 在数组的情况下所做的所有事情都是转换为指针。 std::begin 将简单地返回衰减为指针的数组,即指向第一个元素的指针,std::end 将返回指向最后一个 + 1 元素的指针,或 arr + &lt;length&gt;。这是因为指针是非常好的迭代器,它们遵守所有规则,(用 * 解引用,用 ++ 表示下一个元素,用 -- 表示前一个元素。)这当然不是偶然的,因为迭代器是根据指针建模的。
    • 最后一点; std::beginstd::end 不必为数组参数返回指针,它们也可以返回一些迭代器类,它基本上封装了相同的指针或一些索引变量。他们所做的正是取决于您碰巧拥有的标准库实现,但最好的选择是所有现有实现都将返回指针,因为这是最简单和最有效的实现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-07
    • 1970-01-01
    • 2021-12-30
    • 2017-10-08
    • 2011-09-21
    • 2015-11-29
    • 2021-12-28
    相关资源
    最近更新 更多