Ошибка с дальностью для внутренней функции

У меня есть небольшие проблемы с дальнобойным в 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) {

Почему это случилось? Есть ли простое решение без использования формата шаблона? Есть ли способ, которым я могу использовать аргумент как способ передать массив и информацию о неявном размере?

14

Решение

Циклы 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> поднимая действительный вопрос. Определенно посмотрите на них, особенно с помощью инициализации скобок, вы можете обновить их практически без затрат.

11

Другие решения

«Причина, по которой я думаю, что это может произойти, заключается в том, что я
указатель представления массива некоторая информация теряется, но почему
эта информация потеряна, я не знаю «.

Да. Массивы довольно легко распадаются на указатели, а указатель не знает длины массива. Диапазон для цикла необходим для оценки 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;
}
6

Существует большая разница между массивом и указателем.

Вы можете перебирать массив, используя диапазон на основе, так как размер массива известен во время компиляции. Однако, что вы передаете в функцию — это указатель на первый элемент массива. Размер не известен на этом этапе, поэтому для диапазона не работает.

Во втором примере с шаблоном выгода заключается в том, что вы забыли template <std::size_t len> в определении printListТаким образом, у вас есть 2 разные функции, шаблонная и не шаблонная, которая вызывается.

В этом конкретном случае — я бы рекомендовал использовать более современные std::array

5

такие 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 как размер массива известен. Тем не менее, передача массива в функцию приводит к его распаду на указатель.

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;
}
4

Массив внутри main имеет известный размер.

Однажды перешел на printList функция имеет гнилой на указатель на intс, следовательно, ошибки, которые вы получаете.

3

Вы можете передать массив фиксированного размера в функцию:

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
2
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, либо указатель передачи и размер массив

1
По вопросам рекламы [email protected]