Russian Qt Forum

Программирование => С/C++ => Тема начата: folax от Март 28, 2018, 14:32



Название: std::begin std::end принцип работы
Отправлено: folax от Март 28, 2018, 14:32
Добрый день, нашёл удобный способ находить начало и конец статических массивов. Вопрос почему через функцию не работают данные итераторы begin и end.

Код:
#include <iostream>

using namespace std;

void printArr(int* arr)
{
    int* pBegin = begin(arr);
    int* pEnd   = end(arr);

    while(pBegin != pEnd)
    {
        cout << *pBegin << endl;
        ++pBegin;
    }
}

int main()
{
    int arr[] = { 1, 5, 7, 3, 9, 2 };

    // Почему так можно, каким образом begin и end знают где конце а где начало ?;
    int* pBegin = begin(arr);
    int* pEnd   = end(arr);

    while(pBegin != pEnd)
    {
        cout << *pBegin << endl;
        ++pBegin;
    }

    // А так нельзя!
    printArr(arr);
}


Название: Re: std::begin std::end принцип работы
Отправлено: ViTech от Март 28, 2018, 14:49
Потому что int [] и int * - это совершенно разные типы. В первом случае тип массива и для него есть возможность узнать начало и конец, во втором - простой указатель, который совсем не обязательно на массив указывает. Посмотрите, как в функцию нужно правильно массив передавать. Но со статическими массивами и голыми указателями лучше не работать. То же самое можно получить с помощью std::array.


Название: Re: std::begin std::end принцип работы
Отправлено: folax от Март 28, 2018, 15:41
Я думаю понял по какому принципу работают данные функции. Они берут sizeof(массива) / тип массива, получают размер. Начало это std::begin, конец + 1 это std::end. Только вот немного не могу понять это наверно больше вопрос на тему область видимости. В main мы видим весь массив, а в функции только указатель на его начало, так в функцию массив не передаётся по значению.

Код:
#include <iostream>

using namespace std;

void printArrSize(int arr[])
{
    cout << "Function array size: " << sizeof(arr) << endl;
}

int main()
{
    int arr[] = { 1, 5, 7, 3, 9, 2 };
    cout << "Size: " << sizeof(arr) << endl;
    printArrSize(arr);
}

Ответ: 24
Ответ: 4


Название: Re: std::begin std::end принцип работы
Отправлено: ViTech от Март 28, 2018, 15:58
Это вопрос не на тему области видимости, а на тему типа параметра в сигнатуре функции. Какой тип параметра в функции объявлен, такого подходящего типа аргумент и нужно ей передавать. Вы лучше расскажите, зачем вам такая функция с массивом и что в ней хотите делать.


Название: Re: std::begin std::end принцип работы
Отправлено: Igors от Март 28, 2018, 16:19
Потому что int [] и int * - это совершенно разные типы.
Код
C++ (Qt)
void Foo1( int arr[3] );
void Foo2( int * arr );
 
По-моему эти ф-ции идентичны и код их одинаков. Разница не в типе как таковом, а в rvalue / lvalue. Тут есть академик, он лучше расскажет

Но со статическими массивами и голыми указателями лучше не работать. То же самое можно получить с помощью std::array.
С чем лучше или хуже - зависит от опыта. Если с адресной арифметикой еще не все гладко, то в итераторную срань лучше не лезть.


Название: Re: std::begin std::end принцип работы
Отправлено: folax от Март 28, 2018, 17:19
Это вопрос не на тему области видимости, а на тему типа параметра в сигнатуре функции. Какой тип параметра в функции объявлен, такого подходящего типа аргумент и нужно ей передавать. Вы лучше расскажите, зачем вам такая функция с массивом и что в ней хотите делать.

В начале я хотел функцию чтобы она использовала функции std::begin и std::end для вычисления размера статического массива в экспериментальных целях. Но уже разобрался, выше написал как она работает, как мне кажется. Она не будет работать  через передачу указателя на массив в функцию.
Вся тема вытекла в вопрос на который ответ = Нет нельзя. Вопрос = Можно ли узнать размер статического массива при передачи его в функцию, без указания его размера.

Спасибо большое за ваши ответы и мысли.


Название: Re: std::begin std::end принцип работы
Отправлено: ViTech от Март 28, 2018, 18:56
Если поизвращаться, то можно сделать так:
Код
C++ (Qt)
#include <iostream>
 
using namespace std;
 
template <class Array>
void printArr(Array arr_pointer)
{
   Array& arr    = *reinterpret_cast<Array*>(arr_pointer);
   auto   pBegin = begin(arr);
   auto   pEnd   = end(arr);
 
   while (pBegin != pEnd)
   {
       cout << *pBegin << endl;
       ++pBegin;
   }
}
 
int main()
{
   int arr[] = {1, 5, 7, 3, 9, 2};
 
   printArr<decltype(arr)>(arr);
}
 

Но так лучше не делать :).


Название: Re: std::begin std::end принцип работы
Отправлено: __Heaven__ от Март 28, 2018, 20:07
Вопрос = Можно ли узнать размер статического массива при передачи его в функцию, без указания его размера.
В 17 стандарте позаботились о такой функции.
http://en.cppreference.com/w/cpp/iterator/size


Название: Re: std::begin std::end принцип работы
Отправлено: Igors от Март 29, 2018, 13:08
Можно ли узнать размер статического массива при передачи его в функцию, без указания его размера.
"При передаче" - неясно что имелось виду. Пока компилятор видит rvalue (т.е. объявление самого массива) - можно, в теле самой ф-ции - уже нельзя, там это уже обычный указатель, на сколько эл-тов он указывает неизвестно.

Вывод: от "просто массивов" надо потихоньку уходить (напр с помощью std::array)


Название: Re: std::begin std::end принцип работы
Отправлено: alex312 от Март 29, 2018, 14:19
По-моему эти ф-ции идентичны и код их одинаков. Разница не в типе как таковом, а в rvalue / lvalue. Тут есть академик, он лучше расскажет
Ха, идентичны ?
а чему будет равет sizeof(arr) внутри функции ?


Название: Re: std::begin std::end принцип работы
Отправлено: ViTech от Март 29, 2018, 14:49
Если писать без указателей и извращений, то можно так:
Код
C++ (Qt)
#include <iostream>
 
using namespace std;
 
template <class Array>
void printArr(Array& arr)
{
   auto pBegin = begin(arr);
   auto pEnd   = end(arr);
 
   while (pBegin != pEnd)
   {
       cout << *pBegin << endl;
       ++pBegin;
   }
}
 
int main()
{
   int arr[] = {1, 5, 7, 3, 9, 2};
 
   printArr(arr);
}

Можно такой вариант и дальше улучшать, но всё равно, лучше пользоваться std::array.


Название: Re: std::begin std::end принцип работы
Отправлено: __Heaven__ от Март 29, 2018, 17:53
Предлагаю посмотреть std::initializer_list. Может этого класса будет достаточно.


Название: Re: std::begin std::end принцип работы
Отправлено: Igors от Март 31, 2018, 06:49
Ха, идентичны ?
а чему будет равет sizeof(arr) внутри функции ?
Может лучше самому убедиться (это займет неск минут), а потом уж можно и "Ха"  :)
Передать массив по ссылке можно так
Код
C++ (Qt)
void Foo1( int (& arr)[3] );
 
Все равно передается адрес, но sizeof вернет размер массива, и вызывающий может подать только массив с 3 эл-тами (а не абы какой). А вот как передать массив по значению - не знаю, может такого и нет. Ну завернуть в структуру - то ясно


Название: Re: std::begin std::end принцип работы
Отправлено: __Heaven__ от Апрель 01, 2018, 11:42
Игорь, посыл был в том, что сигнатуры отличаются тем, что в Foo1 известен размер на момент компиляции, в Foo2 не известен.
Код
C++ (Qt)
void Foo1( int arr[3] );
void Foo2( int * arr );
 
По-моему эти ф-ции идентичны и код их одинаков.


Название: Re: std::begin std::end принцип работы
Отправлено: Igors от Апрель 01, 2018, 11:58
Игорь, посыл был в том, что сигнатуры отличаются тем, что в Foo1 известен размер на момент компиляции, в Foo2 не известен.
Код
C++ (Qt)
void Foo1( int arr[3] );
void Foo2( int * arr );
 
По-моему эти ф-ции идентичны и код их одинаков.
Ничего подобного, в обоих случаях неизвестен, оба варианта - передача по указателю. [3] удобно для программиста (мол, планируется 3 эл-та), но компилятору все равно


Название: Re: std::begin std::end принцип работы
Отправлено: kai666_73 от Апрель 01, 2018, 14:56
Игорь, посыл был в том, что сигнатуры отличаются тем, что в Foo1 известен размер на момент компиляции, в Foo2 не известен.
Код
C++ (Qt)
void Foo1( int arr[3] );
void Foo2( int * arr );
 
По-моему эти ф-ции идентичны и код их одинаков.
Ничего подобного, в обоих случаях неизвестен, оба варианта - передача по указателю. [3] удобно для программиста (мол, планируется 3 эл-та), но компилятору все равно
Отвечаете? Прям за все популные нынче компиляторы? При компиляции данного кода как сишный, и как плюсовый?
Мнеб такую уверенность, жилось бы спокойнее 8)


Название: Re: std::begin std::end принцип работы
Отправлено: Igors от Апрель 01, 2018, 17:57
Отвечаете? Прям за все популные нынче компиляторы? При компиляции данного кода как сишный, и как плюсовый?
:)  Ну зачем строчить на форум если проверить - пару минут? Возьмите sizeof(arr) в теле ф-ции, в обоих случаях увидите размер указателя. На каком хотите компиляторе.

Мнеб такую уверенность, жилось бы спокойнее 8)
Как Вы знаете, С++ совместим с С, т.е. программа на С является программой на С++. В С эти конструкции допустимы, стало быть на плюсах тоже, и рез-т должен быть одинаков. Не надо иметь какую-то уверенность, просто верить самому себе   :)


Название: Re: std::begin std::end принцип работы
Отправлено: ViTech от Апрель 01, 2018, 20:34
Как в стандарте языка написано, так компилятор и должен компилять. Вариант ответа (https://stackoverflow.com/a/49214662).


Название: Re: std::begin std::end принцип работы
Отправлено: kai666_73 от Апрель 02, 2018, 12:04
Как Вы знаете, С++ совместим с С, т.е. программа на С является программой на С++. В С эти конструкции допустимы, стало быть на плюсах тоже, и рез-т должен быть одинаков.
Глупости - одинаковый исходный код поданный на вход си-компилятору дает один результат, а на вход плюсовому компилятору - другой. Речь не только о манглинге имен, но и о том какое соглашение о вызове функций будет заиспользовано.

Не стоит воспринимать мой предыдущий пост как наброс на...
Просто я уже сталкивался с ситуациями когда arr[] передовался в функцию как указатель, а arr[3] укладывался целиком в стэк )


Название: Re: std::begin std::end принцип работы
Отправлено: Igors от Апрель 02, 2018, 12:54
Глупости
Если Вы считаете человека глупым - не стоит тратить на него время и слова

- одинаковый исходный код поданный на вход си-компилятору дает один результат, а на вход плюсовому компилятору - другой. Речь не только о манглинге имен, но и о том какое соглашение о вызове функций будет заиспользовано.
Не путайте "код" и "результат"

Просто я уже сталкивался с ситуациями когда arr[] передовался в функцию как указатель, а arr[3] укладывался целиком в стэк )
Так приведите такую ситуацию - или мне уже пора последовать собственному совету выше?  :)


Название: Re: std::begin std::end принцип работы
Отправлено: kai666_73 от Апрель 02, 2018, 15:28
Глупости
Если Вы считаете человека глупым - не стоит тратить на него время и слова
Бывает, что и умный глупость сморозит...


- одинаковый исходный код поданный на вход си-компилятору дает один результат, а на вход плюсовому компилятору - другой. Речь не только о манглинге имен, но и о том какое соглашение о вызове функций будет заиспользовано.
Не путайте "код" и "результат"
Ну дак в конечном счете важен только результат. И в этом контексте "Как Вы знаете, С++ совместим с С, т.е. программа на С является программой на С++." - откровенная глупость


Просто я уже сталкивался с ситуациями когда arr[] передовался в функцию как указатель, а arr[3] укладывался целиком в стэк )
Так приведите такую ситуацию - или мне уже пора последовать собственному совету выше?  :)
Вот уж простите, не упомню...
ЗЫ. Таки да, следуйте, собственным советым - если к прочим глухи