У меня есть небольшие проблемы с дальнобойным в C ++. Я пытаюсь использовать его для отображения элемента в массиве int и int (int []), и он работает совершенно нормально, когда я делаю это в основной функции, как в:
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;
}
Я получаю желаемый и ожидаемый результат, который:
3 4 6 9 2 1
Но все становится немного странным, когда я пытаюсь использовать дальний внутри функции, как пример, у меня проблема с этим кодом:
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;
}
Что для меня такое же, как я делал внутри main, а также с использованием normal для работ совершенно нормально. Странная ошибка заключается в следующем:
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;
}
Я получаю ошибку:
p4.cpp:15:25: error: ‘len’ was not declared in this scope
void printList(int (&v)[len]) {
^
p4.cpp: In function ‘void printList(...)’:
p4.cpp:16:16: error: ‘v’ was not declared in this scope
for (int a : v) {
Почему это случилось? Есть ли простое решение без использования формата шаблона? Есть ли способ, которым я могу использовать аргумент как способ передать массив и информацию о неявном размере?
Циклы 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. конец();
3) В противном случае begin_expr — это начало (диапазон __), а end_expr — это конец (диапазон __), которые можно найти с помощью поиска, зависящего от аргумента (поиск без ADL не выполняется).
Давайте посмотрим, как это относится к вашему делу:
В первом случае v
безусловно, является выражением типа массива или, если быть точным, типа int(&)[6]
поэтому мы используем case (1) где __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>
поднимая действительный вопрос. Определенно посмотрите на них, особенно с помощью инициализации скобок, вы можете обновить их практически без затрат.
«Причина, по которой я думаю, что это может произойти, заключается в том, что я
указатель представления массива некоторая информация теряется, но почему
эта информация потеряна, я не знаю «.
Да. Массивы довольно легко распадаются на указатели, а указатель не знает длины массива. Диапазон для цикла необходим для оценки 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;
}
Существует большая разница между массивом и указателем.
Вы можете перебирать массив, используя диапазон на основе, так как размер массива известен во время компиляции. Однако, что вы передаете в функцию — это указатель на первый элемент массива. Размер не известен на этом этапе, поэтому для диапазона не работает.
Во втором примере с шаблоном выгода заключается в том, что вы забыли template <std::size_t len>
в определении printList
Таким образом, у вас есть 2 разные функции, шаблонная и не шаблонная, которая вызывается.
В этом конкретном случае — я бы рекомендовал использовать более современные std::array
такие for
петли используют begin
а также end
функции-члены, чтобы определить, где находится начало и где конец последовательности.
Например, std::vector
действительно имеет эти функции и, следовательно, поддерживает такой вид итерации. На самом деле указатели — это просто целые числа, которые представляют адреса памяти и не имеют этих функций, что делает невозможным повторение над указателем (что само по себе не имеет особого смысла) таким образом.
Вы могли бы сделать итерацию следующим образом:
void printList(int *begin, int *end) {
for(; begin < end; ++begin)
std::cout << *begin;
std::cout << std::endl;
}
Это работает внутри main
в вашем случае, потому что массивы иметь begin
а также end
как размер массива известен. Тем не менее, передача массива в функцию приводит к его распаду на указатель.
При передаче массива в функцию он распадается на указатель, таким образом он теряет возможность использования с 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;
}
Массив внутри main
имеет известный размер.
Однажды перешел на printList
функция имеет гнилой на указатель на int
с, следовательно, ошибки, которые вы получаете.
Вы можете передать массив фиксированного размера в функцию:
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
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, либо указатель передачи и размер массив