内部函数的ranged错误

时间:2016-11-24 16:35:27

标签: c++ arrays ranged-loops

我在C ++中使用ranged有点麻烦。我正在尝试使用它来显示元素和int数组(int []),当我在main函数上执行它时它完全正常,如:

var y = test.testFunction.bind(test); 
console.log(y());                  // output: 0

我得到了我想要的和预期的输出:

int main(int argc, char const *argv[]) {

  int v[] = {3, 4, 6, 9, 2, 1};

  for (auto a : v) {
      std::cout << a << " ";
  }
  std::cout << std::endl;

  return 0;
}

但是当我尝试在函数内部使用ranged时,事情变得有点奇怪,例如我遇到了这段代码的问题:

3 4 6 9 2 1

对我来说,这与我在main中所做的一样,并且也正常使用普通作品。奇怪的错误如下:

void printList(int *v);

int main(int argc, char const *argv[]) {

  int v[] = {3, 4, 6, 9, 2, 1};

  printList(v);

  return 0;
}

void printList(int *v) {
  for (auto a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

我想知道为什么会发生这种错误,我认为这可能发生的原因是,因为我是数组的指针表示一些信息丢失,但为什么这些信息丢失我不知道。有人深入了解这一点吗?我也在寻找这种替代解决方案:

p4.cpp: In function ‘void printList(int*)’:
p4.cpp:15:17: error: ‘begin’ was not declared in this scope
   for (auto a : v) {
                 ^
p4.cpp:15:17: note: suggested alternative:
In file included from /usr/include/c++/5/string:51:0,
                 from /usr/include/c++/5/bits/locale_classes.h:40,
                 from /usr/include/c++/5/bits/ios_base.h:41,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/ostream:38,
                 from /usr/include/c++/5/iostream:39,
                 from p4.cpp:1:
/usr/include/c++/5/bits/range_access.h:105:37: note:   ‘std::begin’
   template<typename _Tp> const _Tp* begin(const valarray<_Tp>&);
                                     ^
p4.cpp:15:17: error: ‘end’ was not declared in this scope
   for (auto a : v) {
                 ^
p4.cpp:15:17: note: suggested alternative:
In file included from /usr/include/c++/5/string:51:0,
                 from /usr/include/c++/5/bits/locale_classes.h:40,
                 from /usr/include/c++/5/bits/ios_base.h:41,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/ostream:38,
                 from /usr/include/c++/5/iostream:39,
                 from p4.cpp:1:
/usr/include/c++/5/bits/range_access.h:107:37: note:   ‘std::end’
   template<typename _Tp> const _Tp* end(const valarray<_Tp>&);
                                     ^

哪个工作正常,但如果我使用类似的东西:

template <std::size_t len>
void printList(int (&v)[len]) {
  for (int a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

我收到错误:

template <std::size_t len>
void printList(int (&v)[len]);

int main(int argc, char const *argv[]) {
           .........
}

void printList(int (&v)[len]) {
  for (int a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

为什么要发生这种情况?没有使用模板格式有没有简单的解决方案?有没有办法可以使用参数作为传递数组和隐式大小信息的方式?

8 个答案:

答案 0 :(得分:11)

基于范围的for循环本质上只是语法糖,即从cppreference

中检索
  

for ( range_declaration : range_expression ) loop_statement

在功能上等同于以下内容:

{
    auto && __range = range_expression ;
    for (auto __begin = begin_expr, __end = end_expr;
            __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

其中begin_exprend_expr的形成如下

  

1)如果range_expression是数组类型的表达式,则begin_expr是__range,end_expr是(__range + __bound),其中__bound是数组中元素的数量(如果数组具有未知大小或类型不完整) ,该计划是不正确的)

     

2)如果range_expression是类型C的表达式,其具有名为begin的成员和/或名为end的成员(无论此成员的类型或可访问性如何),则begin_expr是__range.begin()和end_expr是__range.end();

     

3)否则,begin_expr开始(__ range),end_expr是end(__ range),它们是通过参数依赖查找找到的(不执行非ADL查找)。

让我们看看这适用于您的情况:

在第一种情况下,v肯定是数组类型的表达式,或者与int(&)[6]类型完全相同,因此我们使用case(1)where __bound = 6等(省略完全扣除的替换)为简洁起见)

在第二种情况下,当你有一个函数时,v的类型为int*,因为它不是数组类型,指针也没有成员,我们默认使用case(3) ADL确定调用begin(__range)的函数,该函数不会产生指针类型的结果,因此编译器会抱怨error: ‘begin’ was not declared in this scope

在第三种情况下,您在尝试定义函数模板printList时出错。您必须保留声明中包含的template<...>部分,否则它只是函数的定义。这就是编译器告诉你error: ‘len’ was not declared in this scope的原因。因此,正确和有效的代码是

template <std::size_t len>
void printList(int (&v)[len]);

int main(int argc, char const *argv[]) {
  int v[] = {3, 4, 6, 9, 2, 1};
  printList(v);
  return 0;
}

template <std::size_t len>
void printList(int (&v)[len]) {
  for (int a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

其他答案已经建议使用不同的容器类型,例如std::array<int, 6>提高有效点。绝对看看它们,特别是使用大括号初始化,您几乎可以免费升级它们。

答案 1 :(得分:6)

  

“我认为这可能发生的原因是,因为我是   数组的指针表示一些信息丢失,但为什么   我不知道这些信息丢失了。“

是。数组很容易衰减成指针,指针不知道数组长度。需要基于for循环的范围来评估数据类型中的begin()end() 我的建议是避免使用C样式数组,而是使用std::array代替:

#include <iostream>
#include <array>

void printList(std::array<int,6> const& v);

int main(int argc, char const *argv[]) {

  std::array<int,6> v{{3, 4, 6, 9, 2, 1}};
  printList(v);

  return 0;
}

void printList(std::array<int,6> const& v) {
  for (auto a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

答案 2 :(得分:5)

数组和指针之间存在很大差异。

您可以使用基于范围的for来迭代数组,因为数组的大小在编译时是已知的。但是,你传递给函数的是 - 指向数组第一个元素的指针。在此步骤中不知道大小,这就是基于范围的失败原因。

在你的第二个带模板的例子中,问题是你在template <std::size_t len>的定义中忘了printList,所以你有2个不同的函数,模板化和非模板化的函数,它们被调用。

在这个确切的情况下 - 我建议使用更现代的std::array

答案 3 :(得分:4)

此类for循环使用beginend成员函数来确定序列的开始位置和结束位置。

例如,std::vector确实具有这些功能,因此支持这种迭代。事实上,指针只是表示内存地址的整数,而没有这些函数,这使得无法在指针上迭代(其中没有&#39这样就可以很有意义了。

你可以这样做迭代:

void printList(int *begin, int *end) {
    for(; begin < end; ++begin)
        std::cout << *begin;
    std::cout << std::endl;
}

这可以在main里面进行您的情况,因为数组确实有beginend 数组的大小已知。但是,将数组传递给函数会使其衰减为指针。

答案 4 :(得分:4)

当一个数组传递给一个函数时,它会衰减到一个指针,因此它失去了与std :: begin和std :: end一起使用的能力。做你想做的事情的现代方法是使用std :: array(如果可能的话,你不应该在C ++中使用C风格的数组):

#include <iostream>
#include <array>

template <typename T, size_t ArraySize>
void printList(const std::array<T, ArraySize>& v)
{
    for (auto a : v) {
        std::cout << a << " ";
    }
    std::cout << std::endl;
}

int main(int argc, char const *argv[]) {

  std::array<int,6> v = {3, 4, 6, 9, 2, 1};

  printList(v);

  return 0;
}    

答案 5 :(得分:3)

main内的数组已知大小。

一旦传递给printList函数,它已衰减指向int的指针,因此会出现错误。

答案 6 :(得分:2)

您可以将固定大小的数组传递给函数:

void printList(int (&v)[6]) {        // pass by reference
    for (auto& a : v) {              // use reference to avoid making a copy
        std::cout << a << " ";
    }
}

然而,当然我们不想编写仅适用于具有特定大小的数组的函数。这是使用模板的意义所在:

template <int size>
void printList(int (&v)[size]) {
    for (auto& a : v) {
        std::cout << a << " ";
    }
}

好的一点是,当你调用函数时,你甚至没有注意到它是一个模板,因为编译器可以推导出模板参数(它知道当然的大小):

int main() {
    int v[] = {3, 4, 6, 9, 2, 1};
    printList(v); 
}

打印:

3 4 6 9 2 1

答案 7 :(得分:1)

int v[] = {3, 4, 6, 9, 2, 1};

得到int[6]的类型,而int *v ..它的类型是int *。您可以使用指向int的指针访问数组,但它不包含有关大小的信息那个数组。 您可以像这样传递数组,但是您将根据数组的大小限制自己:

void foo(int (&p)[6])

或制作模板:

template <std::size_t size> void foo( int (&p)[size] )

如果由于某种原因你不能使用automic for()循环(例如,为了支持C ++ 11 \ 14支持可疑的平台)你需要使用std :: array \ std :: vector或传递指针和数组的大小