我在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;
}
为什么要发生这种情况?没有使用模板格式有没有简单的解决方案?有没有办法可以使用参数作为传递数组和隐式大小信息的方式?
答案 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_expr
和end_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
循环使用begin
和end
成员函数来确定序列的开始位置和结束位置。
例如,std::vector
确实具有这些功能,因此支持这种迭代。事实上,指针只是表示内存地址的整数,而没有这些函数,这使得无法在指针上迭代(其中没有&#39这样就可以很有意义了。
你可以这样做迭代:
void printList(int *begin, int *end) {
for(; begin < end; ++begin)
std::cout << *begin;
std::cout << std::endl;
}
这可以在main
里面进行您的情况,因为数组确实有begin
和end
数组的大小已知。但是,将数组传递给函数会使其衰减为指针。
答案 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或传递指针和数组的大小