【问题标题】:Unrecognized range-based for loop? [duplicate]无法识别的基于范围的for循环? [复制]
【发布时间】:2016-10-15 04:46:06
【问题描述】:

为什么会出现这段代码:

void printarray(int array[]) {
    for (int x: array) {
        std::cout << x << std::endl;
    }
}

生成此编译时错误?

error: 'begin' was not declared in this scope
    for (int x: array) {

我对基于范围的for 循环有什么误解?

【问题讨论】:

  • 在这种情况下int array[] 真的不是一个数组,而是等价于int* array。在函数内部,我们不知道参数的大小。见Sizeof an array in the C programming language
  • 使用最简单的方法:void printarray(std::vector array) {...
  • @Bo Persson 可以内联函数帮助吗?
  • @user - 不,内联不会改变函数的工作方式。只可能是执行速度。

标签: c++ arrays pointers for-loop range


【解决方案1】:

当数组作为函数的参数按值传递时,它会隐式转换为指向其第一个元素的指针。声明数组的参数也调整为指针。

例如这些函数声明

void printarray( int array[100] );
void printarray( int array[10] );
void printarray( int array[] );

声明同一个函数,等价于

void printarray( int *array );

所以你还需要将数组的大小传递给函数,例如

void printarray( const int array[]. size_t n ) 
{
    for ( size_t i = 0; i < n; i++ ) 
    {
        std::cout << a[i] << std::endl;
    }
}

您可以专门为通过引用传递的数组编写模板函数,例如

template <size_t N>
void printarray( const int ( &array )[N] ) 
{
    for ( int x : array) 
    {
        std::cout << x << std::endl;
    }
}

template <typename T, size_t N>
void printarray( const T ( &array )[N] ) 
{
    for ( auto x : array) 
    {
        std::cout << x << std::endl;
    }
}

但是与之前的函数相比,它有一个缺点,因为不同大小的数组是不同的类型,编译器会从模板中生成尽可能多的函数,就像你要与函数一起使用的不同类型的数组一样。

您可以使用标准算法,例如std::copystd::for_each 来输出一个数组。

例如

#include <iostream>
#include <algorithm>
#include <iterator>

int main() 
{
    int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    std::copy( std::begin( array ), std::end( array ), 
               std::ostream_iterator<int>( std::cout, "\n" ) );

    return 0;
}

另一种方法是使用标准类std::array,该类具有适当的成员函数beginend,基于范围的for 语句使用这些函数。例如

#include <iostream>
#include <array>

const size_t N = 10;

void printarray( const std::array<int, N> &array )
{
    for ( int x : array ) std::cout << x << std::endl;
}   

int main() 
{
    std::array<int, N> array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    printarray( array );

    return 0;
}

但在这种情况下,如果您要输出具有不同数量或类型元素的 std::array 类的对象,您还需要编写一个模板函数。

例如

#include <iostream>
#include <array>

template <typename T, size_t N>
void printarray( const std::array<T, N> &array )
{
    for ( auto x : array ) std::cout << x << std::endl;
}   

int main() 
{
    std::array<int, 10> array1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    printarray( array1 );

    std::array<char, 10> array2 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' };

    printarray( array2 );

    return 0;
}

【讨论】:

    【解决方案2】:

    您的问题是 array 实际上不是一个数组。当你写

    void printarray(int array[])
    

    一样
    void printarray(int* array)
    

    如果没有额外的大小参数,您无法判断指针指向多少个元素,因此您无法将它与基于范围的 for 循环一起使用。

    您需要做的是通过引用传递数组,以便数组执行者不会衰减为指针。如果您知道数组的确切大小,则可以使用

    void printarray(int (&array)[size_you_want_here])
    

    如果你想让函数更通用,以便它可以处理不同大小的数组,那么你可以使用类似的模板

    template<std::size_t N>
    void printarray(int (&array)[N])
    

    在上述两种情况下,您现在都有一个实际数组而不是指针,因此您可以将它与基于范围的 for 循环一起使用。

    还请注意,我们可以使函数完全通用

    template<typename T, std::size_t N>
    void printarray(T (&array)[N]) {
        for (auto&& x : array) {
            std::cout << x << "\n";
        }
    }
    

    您还会注意到我将std::endl 更改为"\n"。通常您不想使用endl,因为它在流上显式调用flush()。通常"\n" 是您所需要的,最后如果输出仍未刷新,您可以自己调用flush()

    【讨论】:

    • 为什么auto&amp;&amp; x
    • @user6245072 这只是出于习惯。您也可以使用const auto&amp; 使其只读和引用。
    【解决方案3】:

    printarray 接收的参数实际上是一个 int*,范围不知道在哪里停止。在这种情况下,您需要将长度作为参数发送并执行常规 for

    【讨论】:

      猜你喜欢
      • 2016-10-31
      • 1970-01-01
      • 2018-12-28
      • 2017-11-04
      • 2014-12-06
      • 2011-06-23
      • 2014-01-12
      • 2013-01-04
      • 1970-01-01
      相关资源
      最近更新 更多